11da66f75SEd Tanous /* 21da66f75SEd Tanous // Copyright (c) 2018 Intel Corporation 31da66f75SEd Tanous // 41da66f75SEd Tanous // Licensed under the Apache License, Version 2.0 (the "License"); 51da66f75SEd Tanous // you may not use this file except in compliance with the License. 61da66f75SEd Tanous // You may obtain a copy of the License at 71da66f75SEd Tanous // 81da66f75SEd Tanous // http://www.apache.org/licenses/LICENSE-2.0 91da66f75SEd Tanous // 101da66f75SEd Tanous // Unless required by applicable law or agreed to in writing, software 111da66f75SEd Tanous // distributed under the License is distributed on an "AS IS" BASIS, 121da66f75SEd Tanous // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131da66f75SEd Tanous // See the License for the specific language governing permissions and 141da66f75SEd Tanous // limitations under the License. 151da66f75SEd Tanous */ 161da66f75SEd Tanous #pragma once 171da66f75SEd Tanous 181da66f75SEd Tanous #include "node.hpp" 194851d45dSJason M. Bills #include "registries.hpp" 204851d45dSJason M. Bills #include "registries/base_message_registry.hpp" 214851d45dSJason M. Bills #include "registries/openbmc_message_registry.hpp" 221da66f75SEd Tanous 23e1f26343SJason M. Bills #include <systemd/sd-journal.h> 24e1f26343SJason M. Bills 254851d45dSJason M. Bills #include <boost/algorithm/string/split.hpp> 264851d45dSJason M. Bills #include <boost/beast/core/span.hpp> 271da66f75SEd Tanous #include <boost/container/flat_map.hpp> 281ddcf01aSJason M. Bills #include <boost/system/linux_error.hpp> 29cb92c03bSAndrew Geissler #include <error_messages.hpp> 304418c7f0SJames Feist #include <filesystem> 31cd225da8SJason M. Bills #include <string_view> 32abf2add6SEd Tanous #include <variant> 331da66f75SEd Tanous 341da66f75SEd Tanous namespace redfish 351da66f75SEd Tanous { 361da66f75SEd Tanous 375b61b5e8SJason M. Bills constexpr char const *crashdumpObject = "com.intel.crashdump"; 385b61b5e8SJason M. Bills constexpr char const *crashdumpPath = "/com/intel/crashdump"; 395b61b5e8SJason M. Bills constexpr char const *crashdumpOnDemandPath = "/com/intel/crashdump/OnDemand"; 405b61b5e8SJason M. Bills constexpr char const *crashdumpInterface = "com.intel.crashdump"; 415b61b5e8SJason M. Bills constexpr char const *deleteAllInterface = 425b61b5e8SJason M. Bills "xyz.openbmc_project.Collection.DeleteAll"; 435b61b5e8SJason M. Bills constexpr char const *crashdumpOnDemandInterface = 44424c4176SJason M. Bills "com.intel.crashdump.OnDemand"; 455b61b5e8SJason M. Bills constexpr char const *crashdumpRawPECIInterface = 46424c4176SJason M. Bills "com.intel.crashdump.SendRawPeci"; 471da66f75SEd Tanous 484851d45dSJason M. Bills namespace message_registries 494851d45dSJason M. Bills { 504851d45dSJason M. Bills static const Message *getMessageFromRegistry( 514851d45dSJason M. Bills const std::string &messageKey, 524851d45dSJason M. Bills const boost::beast::span<const MessageEntry> registry) 534851d45dSJason M. Bills { 544851d45dSJason M. Bills boost::beast::span<const MessageEntry>::const_iterator messageIt = 554851d45dSJason M. Bills std::find_if(registry.cbegin(), registry.cend(), 564851d45dSJason M. Bills [&messageKey](const MessageEntry &messageEntry) { 574851d45dSJason M. Bills return !std::strcmp(messageEntry.first, 584851d45dSJason M. Bills messageKey.c_str()); 594851d45dSJason M. Bills }); 604851d45dSJason M. Bills if (messageIt != registry.cend()) 614851d45dSJason M. Bills { 624851d45dSJason M. Bills return &messageIt->second; 634851d45dSJason M. Bills } 644851d45dSJason M. Bills 654851d45dSJason M. Bills return nullptr; 664851d45dSJason M. Bills } 674851d45dSJason M. Bills 684851d45dSJason M. Bills static const Message *getMessage(const std::string_view &messageID) 694851d45dSJason M. Bills { 704851d45dSJason M. Bills // Redfish MessageIds are in the form 714851d45dSJason M. Bills // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find 724851d45dSJason M. Bills // the right Message 734851d45dSJason M. Bills std::vector<std::string> fields; 744851d45dSJason M. Bills fields.reserve(4); 754851d45dSJason M. Bills boost::split(fields, messageID, boost::is_any_of(".")); 764851d45dSJason M. Bills std::string ®istryName = fields[0]; 774851d45dSJason M. Bills std::string &messageKey = fields[3]; 784851d45dSJason M. Bills 794851d45dSJason M. Bills // Find the right registry and check it for the MessageKey 804851d45dSJason M. Bills if (std::string(base::header.registryPrefix) == registryName) 814851d45dSJason M. Bills { 824851d45dSJason M. Bills return getMessageFromRegistry( 834851d45dSJason M. Bills messageKey, boost::beast::span<const MessageEntry>(base::registry)); 844851d45dSJason M. Bills } 854851d45dSJason M. Bills if (std::string(openbmc::header.registryPrefix) == registryName) 864851d45dSJason M. Bills { 874851d45dSJason M. Bills return getMessageFromRegistry( 884851d45dSJason M. Bills messageKey, 894851d45dSJason M. Bills boost::beast::span<const MessageEntry>(openbmc::registry)); 904851d45dSJason M. Bills } 914851d45dSJason M. Bills return nullptr; 924851d45dSJason M. Bills } 934851d45dSJason M. Bills } // namespace message_registries 944851d45dSJason M. Bills 95f6150403SJames Feist namespace fs = std::filesystem; 961da66f75SEd Tanous 97cb92c03bSAndrew Geissler using GetManagedPropertyType = boost::container::flat_map< 98cb92c03bSAndrew Geissler std::string, 99cb92c03bSAndrew Geissler sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t, 100cb92c03bSAndrew Geissler int32_t, uint32_t, int64_t, uint64_t, double>>; 101cb92c03bSAndrew Geissler 102cb92c03bSAndrew Geissler using GetManagedObjectsType = boost::container::flat_map< 103cb92c03bSAndrew Geissler sdbusplus::message::object_path, 104cb92c03bSAndrew Geissler boost::container::flat_map<std::string, GetManagedPropertyType>>; 105cb92c03bSAndrew Geissler 106cb92c03bSAndrew Geissler inline std::string translateSeverityDbusToRedfish(const std::string &s) 107cb92c03bSAndrew Geissler { 108cb92c03bSAndrew Geissler if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert") 109cb92c03bSAndrew Geissler { 110cb92c03bSAndrew Geissler return "Critical"; 111cb92c03bSAndrew Geissler } 112cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") 113cb92c03bSAndrew Geissler { 114cb92c03bSAndrew Geissler return "Critical"; 115cb92c03bSAndrew Geissler } 116cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug") 117cb92c03bSAndrew Geissler { 118cb92c03bSAndrew Geissler return "OK"; 119cb92c03bSAndrew Geissler } 120cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") 121cb92c03bSAndrew Geissler { 122cb92c03bSAndrew Geissler return "Critical"; 123cb92c03bSAndrew Geissler } 124cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error") 125cb92c03bSAndrew Geissler { 126cb92c03bSAndrew Geissler return "Critical"; 127cb92c03bSAndrew Geissler } 128cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") 129cb92c03bSAndrew Geissler { 130cb92c03bSAndrew Geissler return "OK"; 131cb92c03bSAndrew Geissler } 132cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice") 133cb92c03bSAndrew Geissler { 134cb92c03bSAndrew Geissler return "OK"; 135cb92c03bSAndrew Geissler } 136cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning") 137cb92c03bSAndrew Geissler { 138cb92c03bSAndrew Geissler return "Warning"; 139cb92c03bSAndrew Geissler } 140cb92c03bSAndrew Geissler return ""; 141cb92c03bSAndrew Geissler } 142cb92c03bSAndrew Geissler 14316428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal, 14439e77504SEd Tanous const std::string_view &field, 14539e77504SEd Tanous std::string_view &contents) 14616428a1aSJason M. Bills { 14716428a1aSJason M. Bills const char *data = nullptr; 14816428a1aSJason M. Bills size_t length = 0; 14916428a1aSJason M. Bills int ret = 0; 15016428a1aSJason M. Bills // Get the metadata from the requested field of the journal entry 151271584abSEd Tanous ret = sd_journal_get_data(journal, field.data(), 152271584abSEd Tanous reinterpret_cast<const void **>(&data), &length); 15316428a1aSJason M. Bills if (ret < 0) 15416428a1aSJason M. Bills { 15516428a1aSJason M. Bills return ret; 15616428a1aSJason M. Bills } 15739e77504SEd Tanous contents = std::string_view(data, length); 15816428a1aSJason M. Bills // Only use the content after the "=" character. 15916428a1aSJason M. Bills contents.remove_prefix(std::min(contents.find("=") + 1, contents.size())); 16016428a1aSJason M. Bills return ret; 16116428a1aSJason M. Bills } 16216428a1aSJason M. Bills 16316428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal, 16439e77504SEd Tanous const std::string_view &field, const int &base, 165271584abSEd Tanous long int &contents) 16616428a1aSJason M. Bills { 16716428a1aSJason M. Bills int ret = 0; 16839e77504SEd Tanous std::string_view metadata; 16916428a1aSJason M. Bills // Get the metadata from the requested field of the journal entry 17016428a1aSJason M. Bills ret = getJournalMetadata(journal, field, metadata); 17116428a1aSJason M. Bills if (ret < 0) 17216428a1aSJason M. Bills { 17316428a1aSJason M. Bills return ret; 17416428a1aSJason M. Bills } 175b01bf299SEd Tanous contents = strtol(metadata.data(), nullptr, base); 17616428a1aSJason M. Bills return ret; 17716428a1aSJason M. Bills } 17816428a1aSJason M. Bills 17916428a1aSJason M. Bills static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp) 18016428a1aSJason M. Bills { 18116428a1aSJason M. Bills int ret = 0; 18216428a1aSJason M. Bills uint64_t timestamp = 0; 18316428a1aSJason M. Bills ret = sd_journal_get_realtime_usec(journal, ×tamp); 18416428a1aSJason M. Bills if (ret < 0) 18516428a1aSJason M. Bills { 18616428a1aSJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 18716428a1aSJason M. Bills << strerror(-ret); 18816428a1aSJason M. Bills return false; 18916428a1aSJason M. Bills } 19016428a1aSJason M. Bills time_t t = 19116428a1aSJason M. Bills static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s 19216428a1aSJason M. Bills struct tm *loctime = localtime(&t); 19316428a1aSJason M. Bills char entryTime[64] = {}; 19499131cd0SEd Tanous if (nullptr != loctime) 19516428a1aSJason M. Bills { 19616428a1aSJason M. Bills strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime); 19716428a1aSJason M. Bills } 19816428a1aSJason M. Bills // Insert the ':' into the timezone 19939e77504SEd Tanous std::string_view t1(entryTime); 20039e77504SEd Tanous std::string_view t2(entryTime); 20116428a1aSJason M. Bills if (t1.size() > 2 && t2.size() > 2) 20216428a1aSJason M. Bills { 20316428a1aSJason M. Bills t1.remove_suffix(2); 20416428a1aSJason M. Bills t2.remove_prefix(t2.size() - 2); 20516428a1aSJason M. Bills } 20639e77504SEd Tanous entryTimestamp = std::string(t1) + ":" + std::string(t2); 20716428a1aSJason M. Bills return true; 20816428a1aSJason M. Bills } 20916428a1aSJason M. Bills 21016428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req, 211271584abSEd Tanous uint64_t &skip) 21216428a1aSJason M. Bills { 21316428a1aSJason M. Bills char *skipParam = req.urlParams.get("$skip"); 21416428a1aSJason M. Bills if (skipParam != nullptr) 21516428a1aSJason M. Bills { 21616428a1aSJason M. Bills char *ptr = nullptr; 217271584abSEd Tanous skip = std::strtoul(skipParam, &ptr, 10); 21816428a1aSJason M. Bills if (*skipParam == '\0' || *ptr != '\0') 21916428a1aSJason M. Bills { 22016428a1aSJason M. Bills 22116428a1aSJason M. Bills messages::queryParameterValueTypeError(res, std::string(skipParam), 22216428a1aSJason M. Bills "$skip"); 22316428a1aSJason M. Bills return false; 22416428a1aSJason M. Bills } 22516428a1aSJason M. Bills } 22616428a1aSJason M. Bills return true; 22716428a1aSJason M. Bills } 22816428a1aSJason M. Bills 229271584abSEd Tanous static constexpr const uint64_t maxEntriesPerPage = 1000; 23016428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req, 231271584abSEd Tanous uint64_t &top) 23216428a1aSJason M. Bills { 23316428a1aSJason M. Bills char *topParam = req.urlParams.get("$top"); 23416428a1aSJason M. Bills if (topParam != nullptr) 23516428a1aSJason M. Bills { 23616428a1aSJason M. Bills char *ptr = nullptr; 237271584abSEd Tanous top = std::strtoul(topParam, &ptr, 10); 23816428a1aSJason M. Bills if (*topParam == '\0' || *ptr != '\0') 23916428a1aSJason M. Bills { 24016428a1aSJason M. Bills messages::queryParameterValueTypeError(res, std::string(topParam), 24116428a1aSJason M. Bills "$top"); 24216428a1aSJason M. Bills return false; 24316428a1aSJason M. Bills } 244271584abSEd Tanous if (top < 1U || top > maxEntriesPerPage) 24516428a1aSJason M. Bills { 24616428a1aSJason M. Bills 24716428a1aSJason M. Bills messages::queryParameterOutOfRange( 24816428a1aSJason M. Bills res, std::to_string(top), "$top", 24916428a1aSJason M. Bills "1-" + std::to_string(maxEntriesPerPage)); 25016428a1aSJason M. Bills return false; 25116428a1aSJason M. Bills } 25216428a1aSJason M. Bills } 25316428a1aSJason M. Bills return true; 25416428a1aSJason M. Bills } 25516428a1aSJason M. Bills 256e85d6b16SJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID, 257e85d6b16SJason M. Bills const bool firstEntry = true) 25816428a1aSJason M. Bills { 25916428a1aSJason M. Bills int ret = 0; 26016428a1aSJason M. Bills static uint64_t prevTs = 0; 26116428a1aSJason M. Bills static int index = 0; 262e85d6b16SJason M. Bills if (firstEntry) 263e85d6b16SJason M. Bills { 264e85d6b16SJason M. Bills prevTs = 0; 265e85d6b16SJason M. Bills } 266e85d6b16SJason M. Bills 26716428a1aSJason M. Bills // Get the entry timestamp 26816428a1aSJason M. Bills uint64_t curTs = 0; 26916428a1aSJason M. Bills ret = sd_journal_get_realtime_usec(journal, &curTs); 27016428a1aSJason M. Bills if (ret < 0) 27116428a1aSJason M. Bills { 27216428a1aSJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 27316428a1aSJason M. Bills << strerror(-ret); 27416428a1aSJason M. Bills return false; 27516428a1aSJason M. Bills } 27616428a1aSJason M. Bills // If the timestamp isn't unique, increment the index 27716428a1aSJason M. Bills if (curTs == prevTs) 27816428a1aSJason M. Bills { 27916428a1aSJason M. Bills index++; 28016428a1aSJason M. Bills } 28116428a1aSJason M. Bills else 28216428a1aSJason M. Bills { 28316428a1aSJason M. Bills // Otherwise, reset it 28416428a1aSJason M. Bills index = 0; 28516428a1aSJason M. Bills } 28616428a1aSJason M. Bills // Save the timestamp 28716428a1aSJason M. Bills prevTs = curTs; 28816428a1aSJason M. Bills 28916428a1aSJason M. Bills entryID = std::to_string(curTs); 29016428a1aSJason M. Bills if (index > 0) 29116428a1aSJason M. Bills { 29216428a1aSJason M. Bills entryID += "_" + std::to_string(index); 29316428a1aSJason M. Bills } 29416428a1aSJason M. Bills return true; 29516428a1aSJason M. Bills } 29616428a1aSJason M. Bills 297e85d6b16SJason M. Bills static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID, 298e85d6b16SJason M. Bills const bool firstEntry = true) 29995820184SJason M. Bills { 300271584abSEd Tanous static time_t prevTs = 0; 30195820184SJason M. Bills static int index = 0; 302e85d6b16SJason M. Bills if (firstEntry) 303e85d6b16SJason M. Bills { 304e85d6b16SJason M. Bills prevTs = 0; 305e85d6b16SJason M. Bills } 306e85d6b16SJason M. Bills 30795820184SJason M. Bills // Get the entry timestamp 308271584abSEd Tanous std::time_t curTs = 0; 30995820184SJason M. Bills std::tm timeStruct = {}; 31095820184SJason M. Bills std::istringstream entryStream(logEntry); 31195820184SJason M. Bills if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 31295820184SJason M. Bills { 31395820184SJason M. Bills curTs = std::mktime(&timeStruct); 31495820184SJason M. Bills } 31595820184SJason M. Bills // If the timestamp isn't unique, increment the index 31695820184SJason M. Bills if (curTs == prevTs) 31795820184SJason M. Bills { 31895820184SJason M. Bills index++; 31995820184SJason M. Bills } 32095820184SJason M. Bills else 32195820184SJason M. Bills { 32295820184SJason M. Bills // Otherwise, reset it 32395820184SJason M. Bills index = 0; 32495820184SJason M. Bills } 32595820184SJason M. Bills // Save the timestamp 32695820184SJason M. Bills prevTs = curTs; 32795820184SJason M. Bills 32895820184SJason M. Bills entryID = std::to_string(curTs); 32995820184SJason M. Bills if (index > 0) 33095820184SJason M. Bills { 33195820184SJason M. Bills entryID += "_" + std::to_string(index); 33295820184SJason M. Bills } 33395820184SJason M. Bills return true; 33495820184SJason M. Bills } 33595820184SJason M. Bills 33616428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID, 337271584abSEd Tanous uint64_t ×tamp, uint64_t &index) 33816428a1aSJason M. Bills { 33916428a1aSJason M. Bills if (entryID.empty()) 34016428a1aSJason M. Bills { 34116428a1aSJason M. Bills return false; 34216428a1aSJason M. Bills } 34316428a1aSJason M. Bills // Convert the unique ID back to a timestamp to find the entry 34439e77504SEd Tanous std::string_view tsStr(entryID); 34516428a1aSJason M. Bills 34616428a1aSJason M. Bills auto underscorePos = tsStr.find("_"); 34716428a1aSJason M. Bills if (underscorePos != tsStr.npos) 34816428a1aSJason M. Bills { 34916428a1aSJason M. Bills // Timestamp has an index 35016428a1aSJason M. Bills tsStr.remove_suffix(tsStr.size() - underscorePos); 35139e77504SEd Tanous std::string_view indexStr(entryID); 35216428a1aSJason M. Bills indexStr.remove_prefix(underscorePos + 1); 35316428a1aSJason M. Bills std::size_t pos; 35416428a1aSJason M. Bills try 35516428a1aSJason M. Bills { 35639e77504SEd Tanous index = std::stoul(std::string(indexStr), &pos); 35716428a1aSJason M. Bills } 358271584abSEd Tanous catch (std::invalid_argument &) 35916428a1aSJason M. Bills { 36016428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 36116428a1aSJason M. Bills return false; 36216428a1aSJason M. Bills } 363271584abSEd Tanous catch (std::out_of_range &) 36416428a1aSJason M. Bills { 36516428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 36616428a1aSJason M. Bills return false; 36716428a1aSJason M. Bills } 36816428a1aSJason M. Bills if (pos != indexStr.size()) 36916428a1aSJason M. Bills { 37016428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 37116428a1aSJason M. Bills return false; 37216428a1aSJason M. Bills } 37316428a1aSJason M. Bills } 37416428a1aSJason M. Bills // Timestamp has no index 37516428a1aSJason M. Bills std::size_t pos; 37616428a1aSJason M. Bills try 37716428a1aSJason M. Bills { 37839e77504SEd Tanous timestamp = std::stoull(std::string(tsStr), &pos); 37916428a1aSJason M. Bills } 380271584abSEd Tanous catch (std::invalid_argument &) 38116428a1aSJason M. Bills { 38216428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 38316428a1aSJason M. Bills return false; 38416428a1aSJason M. Bills } 385271584abSEd Tanous catch (std::out_of_range &) 38616428a1aSJason M. Bills { 38716428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 38816428a1aSJason M. Bills return false; 38916428a1aSJason M. Bills } 39016428a1aSJason M. Bills if (pos != tsStr.size()) 39116428a1aSJason M. Bills { 39216428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 39316428a1aSJason M. Bills return false; 39416428a1aSJason M. Bills } 39516428a1aSJason M. Bills return true; 39616428a1aSJason M. Bills } 39716428a1aSJason M. Bills 39895820184SJason M. Bills static bool 39995820184SJason M. Bills getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles) 40095820184SJason M. Bills { 40195820184SJason M. Bills static const std::filesystem::path redfishLogDir = "/var/log"; 40295820184SJason M. Bills static const std::string redfishLogFilename = "redfish"; 40395820184SJason M. Bills 40495820184SJason M. Bills // Loop through the directory looking for redfish log files 40595820184SJason M. Bills for (const std::filesystem::directory_entry &dirEnt : 40695820184SJason M. Bills std::filesystem::directory_iterator(redfishLogDir)) 40795820184SJason M. Bills { 40895820184SJason M. Bills // If we find a redfish log file, save the path 40995820184SJason M. Bills std::string filename = dirEnt.path().filename(); 41095820184SJason M. Bills if (boost::starts_with(filename, redfishLogFilename)) 41195820184SJason M. Bills { 41295820184SJason M. Bills redfishLogFiles.emplace_back(redfishLogDir / filename); 41395820184SJason M. Bills } 41495820184SJason M. Bills } 41595820184SJason M. Bills // As the log files rotate, they are appended with a ".#" that is higher for 41695820184SJason M. Bills // the older logs. Since we don't expect more than 10 log files, we 41795820184SJason M. Bills // can just sort the list to get them in order from newest to oldest 41895820184SJason M. Bills std::sort(redfishLogFiles.begin(), redfishLogFiles.end()); 41995820184SJason M. Bills 42095820184SJason M. Bills return !redfishLogFiles.empty(); 42195820184SJason M. Bills } 42295820184SJason M. Bills 423c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node 4241da66f75SEd Tanous { 4251da66f75SEd Tanous public: 4261da66f75SEd Tanous template <typename CrowApp> 427c4bf6374SJason M. Bills SystemLogServiceCollection(CrowApp &app) : 428029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/") 429c4bf6374SJason M. Bills { 430c4bf6374SJason M. Bills entityPrivileges = { 431c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 432c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 433c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 434c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 435c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 436c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 437c4bf6374SJason M. Bills } 438c4bf6374SJason M. Bills 439c4bf6374SJason M. Bills private: 440c4bf6374SJason M. Bills /** 441c4bf6374SJason M. Bills * Functions triggers appropriate requests on DBus 442c4bf6374SJason M. Bills */ 443c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 444c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 445c4bf6374SJason M. Bills { 446c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 447c4bf6374SJason M. Bills // Collections don't include the static data added by SubRoute because 448c4bf6374SJason M. Bills // it has a duplicate entry for members 449c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 450c4bf6374SJason M. Bills "#LogServiceCollection.LogServiceCollection"; 451c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 452c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection"; 453c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 454029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices"; 455c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "System Log Services Collection"; 456c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = 457c4bf6374SJason M. Bills "Collection of LogServices for this Computer System"; 458c4bf6374SJason M. Bills nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; 459c4bf6374SJason M. Bills logServiceArray = nlohmann::json::array(); 460029573d4SEd Tanous logServiceArray.push_back( 461029573d4SEd Tanous {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}}); 462d53dd41fSJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG 463d53dd41fSJason M. Bills logServiceArray.push_back( 464cb92c03bSAndrew Geissler {{"@odata.id", 465424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump"}}); 466d53dd41fSJason M. Bills #endif 467c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 468c4bf6374SJason M. Bills logServiceArray.size(); 469c4bf6374SJason M. Bills } 470c4bf6374SJason M. Bills }; 471c4bf6374SJason M. Bills 472c4bf6374SJason M. Bills class EventLogService : public Node 473c4bf6374SJason M. Bills { 474c4bf6374SJason M. Bills public: 475c4bf6374SJason M. Bills template <typename CrowApp> 476c4bf6374SJason M. Bills EventLogService(CrowApp &app) : 477029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/") 478c4bf6374SJason M. Bills { 479c4bf6374SJason M. Bills entityPrivileges = { 480c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 481c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 482c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 483c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 484c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 485c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 486c4bf6374SJason M. Bills } 487c4bf6374SJason M. Bills 488c4bf6374SJason M. Bills private: 489c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 490c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 491c4bf6374SJason M. Bills { 492c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 493c4bf6374SJason M. Bills 494c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 495029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog"; 496c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 497c4bf6374SJason M. Bills "#LogService.v1_1_0.LogService"; 498c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 499c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 500c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "Event Log Service"; 501c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = "System Event Log Service"; 502c4bf6374SJason M. Bills asyncResp->res.jsonValue["Id"] = "Event Log"; 503c4bf6374SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 504c4bf6374SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 505c4bf6374SJason M. Bills {"@odata.id", 506029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}}; 507e7d6c8b2SGunnar Mills asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = { 508e7d6c8b2SGunnar Mills 509e7d6c8b2SGunnar Mills {"target", "/redfish/v1/Systems/system/LogServices/EventLog/" 510e7d6c8b2SGunnar Mills "Actions/LogService.ClearLog"}}; 511489640c6SJason M. Bills } 512489640c6SJason M. Bills }; 513489640c6SJason M. Bills 5141f56a3a6STim Lee class JournalEventLogClear : public Node 515489640c6SJason M. Bills { 516489640c6SJason M. Bills public: 5171f56a3a6STim Lee JournalEventLogClear(CrowApp &app) : 518489640c6SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/" 519489640c6SJason M. Bills "LogService.ClearLog/") 520489640c6SJason M. Bills { 521489640c6SJason M. Bills entityPrivileges = { 522489640c6SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 523489640c6SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 524489640c6SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 525489640c6SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 526489640c6SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 527489640c6SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 528489640c6SJason M. Bills } 529489640c6SJason M. Bills 530489640c6SJason M. Bills private: 531489640c6SJason M. Bills void doPost(crow::Response &res, const crow::Request &req, 532489640c6SJason M. Bills const std::vector<std::string> ¶ms) override 533489640c6SJason M. Bills { 534489640c6SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 535489640c6SJason M. Bills 536489640c6SJason M. Bills // Clear the EventLog by deleting the log files 537489640c6SJason M. Bills std::vector<std::filesystem::path> redfishLogFiles; 538489640c6SJason M. Bills if (getRedfishLogFiles(redfishLogFiles)) 539489640c6SJason M. Bills { 540489640c6SJason M. Bills for (const std::filesystem::path &file : redfishLogFiles) 541489640c6SJason M. Bills { 542489640c6SJason M. Bills std::error_code ec; 543489640c6SJason M. Bills std::filesystem::remove(file, ec); 544489640c6SJason M. Bills } 545489640c6SJason M. Bills } 546489640c6SJason M. Bills 547489640c6SJason M. Bills // Reload rsyslog so it knows to start new log files 548489640c6SJason M. Bills crow::connections::systemBus->async_method_call( 549489640c6SJason M. Bills [asyncResp](const boost::system::error_code ec) { 550489640c6SJason M. Bills if (ec) 551489640c6SJason M. Bills { 552489640c6SJason M. Bills BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec; 553489640c6SJason M. Bills messages::internalError(asyncResp->res); 554489640c6SJason M. Bills return; 555489640c6SJason M. Bills } 556489640c6SJason M. Bills 557489640c6SJason M. Bills messages::success(asyncResp->res); 558489640c6SJason M. Bills }, 559489640c6SJason M. Bills "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 560489640c6SJason M. Bills "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service", 561489640c6SJason M. Bills "replace"); 562c4bf6374SJason M. Bills } 563c4bf6374SJason M. Bills }; 564c4bf6374SJason M. Bills 56595820184SJason M. Bills static int fillEventLogEntryJson(const std::string &logEntryID, 56695820184SJason M. Bills const std::string logEntry, 56795820184SJason M. Bills nlohmann::json &logEntryJson) 568c4bf6374SJason M. Bills { 56995820184SJason M. Bills // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 570cd225da8SJason M. Bills // First get the Timestamp 571cd225da8SJason M. Bills size_t space = logEntry.find_first_of(" "); 572cd225da8SJason M. Bills if (space == std::string::npos) 57395820184SJason M. Bills { 57495820184SJason M. Bills return 1; 57595820184SJason M. Bills } 576cd225da8SJason M. Bills std::string timestamp = logEntry.substr(0, space); 577cd225da8SJason M. Bills // Then get the log contents 578cd225da8SJason M. Bills size_t entryStart = logEntry.find_first_not_of(" ", space); 579cd225da8SJason M. Bills if (entryStart == std::string::npos) 580cd225da8SJason M. Bills { 581cd225da8SJason M. Bills return 1; 582cd225da8SJason M. Bills } 583cd225da8SJason M. Bills std::string_view entry(logEntry); 584cd225da8SJason M. Bills entry.remove_prefix(entryStart); 585cd225da8SJason M. Bills // Use split to separate the entry into its fields 586cd225da8SJason M. Bills std::vector<std::string> logEntryFields; 587cd225da8SJason M. Bills boost::split(logEntryFields, entry, boost::is_any_of(","), 588cd225da8SJason M. Bills boost::token_compress_on); 589cd225da8SJason M. Bills // We need at least a MessageId to be valid 590cd225da8SJason M. Bills if (logEntryFields.size() < 1) 591cd225da8SJason M. Bills { 592cd225da8SJason M. Bills return 1; 593cd225da8SJason M. Bills } 594cd225da8SJason M. Bills std::string &messageID = logEntryFields[0]; 59595820184SJason M. Bills 5964851d45dSJason M. Bills // Get the Message from the MessageRegistry 5974851d45dSJason M. Bills const message_registries::Message *message = 5984851d45dSJason M. Bills message_registries::getMessage(messageID); 599c4bf6374SJason M. Bills 6004851d45dSJason M. Bills std::string msg; 6014851d45dSJason M. Bills std::string severity; 6024851d45dSJason M. Bills if (message != nullptr) 603c4bf6374SJason M. Bills { 6044851d45dSJason M. Bills msg = message->message; 6054851d45dSJason M. Bills severity = message->severity; 606c4bf6374SJason M. Bills } 607c4bf6374SJason M. Bills 60815a86ff6SJason M. Bills // Get the MessageArgs from the log if there are any 60915a86ff6SJason M. Bills boost::beast::span<std::string> messageArgs; 61015a86ff6SJason M. Bills if (logEntryFields.size() > 1) 61115a86ff6SJason M. Bills { 61215a86ff6SJason M. Bills std::string &messageArgsStart = logEntryFields[1]; 61315a86ff6SJason M. Bills // If the first string is empty, assume there are no MessageArgs 61415a86ff6SJason M. Bills std::size_t messageArgsSize = 0; 61515a86ff6SJason M. Bills if (!messageArgsStart.empty()) 61615a86ff6SJason M. Bills { 61715a86ff6SJason M. Bills messageArgsSize = logEntryFields.size() - 1; 61815a86ff6SJason M. Bills } 61915a86ff6SJason M. Bills 62015a86ff6SJason M. Bills messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize); 621c4bf6374SJason M. Bills 6224851d45dSJason M. Bills // Fill the MessageArgs into the Message 62395820184SJason M. Bills int i = 0; 62495820184SJason M. Bills for (const std::string &messageArg : messageArgs) 6254851d45dSJason M. Bills { 62695820184SJason M. Bills std::string argStr = "%" + std::to_string(++i); 6274851d45dSJason M. Bills size_t argPos = msg.find(argStr); 6284851d45dSJason M. Bills if (argPos != std::string::npos) 6294851d45dSJason M. Bills { 63095820184SJason M. Bills msg.replace(argPos, argStr.length(), messageArg); 6314851d45dSJason M. Bills } 6324851d45dSJason M. Bills } 63315a86ff6SJason M. Bills } 6344851d45dSJason M. Bills 63595820184SJason M. Bills // Get the Created time from the timestamp. The log timestamp is in RFC3339 63695820184SJason M. Bills // format which matches the Redfish format except for the fractional seconds 63795820184SJason M. Bills // between the '.' and the '+', so just remove them. 63895820184SJason M. Bills std::size_t dot = timestamp.find_first_of("."); 63995820184SJason M. Bills std::size_t plus = timestamp.find_first_of("+"); 64095820184SJason M. Bills if (dot != std::string::npos && plus != std::string::npos) 641c4bf6374SJason M. Bills { 64295820184SJason M. Bills timestamp.erase(dot, plus - dot); 643c4bf6374SJason M. Bills } 644c4bf6374SJason M. Bills 645c4bf6374SJason M. Bills // Fill in the log entry with the gathered data 64695820184SJason M. Bills logEntryJson = { 647cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 648c4bf6374SJason M. Bills {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 649029573d4SEd Tanous {"@odata.id", 650897967deSJason M. Bills "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + 65195820184SJason M. Bills logEntryID}, 652c4bf6374SJason M. Bills {"Name", "System Event Log Entry"}, 65395820184SJason M. Bills {"Id", logEntryID}, 65495820184SJason M. Bills {"Message", std::move(msg)}, 65595820184SJason M. Bills {"MessageId", std::move(messageID)}, 656c4bf6374SJason M. Bills {"MessageArgs", std::move(messageArgs)}, 657c4bf6374SJason M. Bills {"EntryType", "Event"}, 65895820184SJason M. Bills {"Severity", std::move(severity)}, 65995820184SJason M. Bills {"Created", std::move(timestamp)}}; 660c4bf6374SJason M. Bills return 0; 661c4bf6374SJason M. Bills } 662c4bf6374SJason M. Bills 66327062605SAnthony Wilson class JournalEventLogEntryCollection : public Node 664c4bf6374SJason M. Bills { 665c4bf6374SJason M. Bills public: 666c4bf6374SJason M. Bills template <typename CrowApp> 66727062605SAnthony Wilson JournalEventLogEntryCollection(CrowApp &app) : 668029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/") 669c4bf6374SJason M. Bills { 670c4bf6374SJason M. Bills entityPrivileges = { 671c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 672c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 673c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 674c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 675c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 676c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 677c4bf6374SJason M. Bills } 678c4bf6374SJason M. Bills 679c4bf6374SJason M. Bills private: 680c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 681c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 682c4bf6374SJason M. Bills { 683c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 684271584abSEd Tanous uint64_t skip = 0; 685271584abSEd Tanous uint64_t top = maxEntriesPerPage; // Show max entries by default 686c4bf6374SJason M. Bills if (!getSkipParam(asyncResp->res, req, skip)) 687c4bf6374SJason M. Bills { 688c4bf6374SJason M. Bills return; 689c4bf6374SJason M. Bills } 690c4bf6374SJason M. Bills if (!getTopParam(asyncResp->res, req, top)) 691c4bf6374SJason M. Bills { 692c4bf6374SJason M. Bills return; 693c4bf6374SJason M. Bills } 694c4bf6374SJason M. Bills // Collections don't include the static data added by SubRoute because 695c4bf6374SJason M. Bills // it has a duplicate entry for members 696c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 697c4bf6374SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 698c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 699c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection"; 700c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 701029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries"; 702c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 703c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = 704c4bf6374SJason M. Bills "Collection of System Event Log Entries"; 705cb92c03bSAndrew Geissler 706c4bf6374SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 707c4bf6374SJason M. Bills logEntryArray = nlohmann::json::array(); 70895820184SJason M. Bills // Go through the log files and create a unique ID for each entry 70995820184SJason M. Bills std::vector<std::filesystem::path> redfishLogFiles; 71095820184SJason M. Bills getRedfishLogFiles(redfishLogFiles); 711b01bf299SEd Tanous uint64_t entryCount = 0; 712cd225da8SJason M. Bills std::string logEntry; 71395820184SJason M. Bills 71495820184SJason M. Bills // Oldest logs are in the last file, so start there and loop backwards 715cd225da8SJason M. Bills for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); 716cd225da8SJason M. Bills it++) 717c4bf6374SJason M. Bills { 718cd225da8SJason M. Bills std::ifstream logStream(*it); 71995820184SJason M. Bills if (!logStream.is_open()) 720c4bf6374SJason M. Bills { 721c4bf6374SJason M. Bills continue; 722c4bf6374SJason M. Bills } 723c4bf6374SJason M. Bills 724e85d6b16SJason M. Bills // Reset the unique ID on the first entry 725e85d6b16SJason M. Bills bool firstEntry = true; 72695820184SJason M. Bills while (std::getline(logStream, logEntry)) 72795820184SJason M. Bills { 728c4bf6374SJason M. Bills entryCount++; 729c4bf6374SJason M. Bills // Handle paging using skip (number of entries to skip from the 730c4bf6374SJason M. Bills // start) and top (number of entries to display) 731c4bf6374SJason M. Bills if (entryCount <= skip || entryCount > skip + top) 732c4bf6374SJason M. Bills { 733c4bf6374SJason M. Bills continue; 734c4bf6374SJason M. Bills } 735c4bf6374SJason M. Bills 736c4bf6374SJason M. Bills std::string idStr; 737e85d6b16SJason M. Bills if (!getUniqueEntryID(logEntry, idStr, firstEntry)) 738c4bf6374SJason M. Bills { 739c4bf6374SJason M. Bills continue; 740c4bf6374SJason M. Bills } 741c4bf6374SJason M. Bills 742e85d6b16SJason M. Bills if (firstEntry) 743e85d6b16SJason M. Bills { 744e85d6b16SJason M. Bills firstEntry = false; 745e85d6b16SJason M. Bills } 746e85d6b16SJason M. Bills 747c4bf6374SJason M. Bills logEntryArray.push_back({}); 748c4bf6374SJason M. Bills nlohmann::json &bmcLogEntry = logEntryArray.back(); 74995820184SJason M. Bills if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0) 750c4bf6374SJason M. Bills { 751c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 752c4bf6374SJason M. Bills return; 753c4bf6374SJason M. Bills } 754c4bf6374SJason M. Bills } 75595820184SJason M. Bills } 756c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 757c4bf6374SJason M. Bills if (skip + top < entryCount) 758c4bf6374SJason M. Bills { 759c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 76095820184SJason M. Bills "/redfish/v1/Systems/system/LogServices/EventLog/" 76195820184SJason M. Bills "Entries?$skip=" + 762c4bf6374SJason M. Bills std::to_string(skip + top); 763c4bf6374SJason M. Bills } 76408a4e4b5SAnthony Wilson } 76508a4e4b5SAnthony Wilson }; 76608a4e4b5SAnthony Wilson 767897967deSJason M. Bills class JournalEventLogEntry : public Node 768897967deSJason M. Bills { 769897967deSJason M. Bills public: 770897967deSJason M. Bills JournalEventLogEntry(CrowApp &app) : 771897967deSJason M. Bills Node(app, 772897967deSJason M. Bills "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/", 773897967deSJason M. Bills std::string()) 774897967deSJason M. Bills { 775897967deSJason M. Bills entityPrivileges = { 776897967deSJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 777897967deSJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 778897967deSJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 779897967deSJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 780897967deSJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 781897967deSJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 782897967deSJason M. Bills } 783897967deSJason M. Bills 784897967deSJason M. Bills private: 785897967deSJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 786897967deSJason M. Bills const std::vector<std::string> ¶ms) override 787897967deSJason M. Bills { 788897967deSJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 789897967deSJason M. Bills if (params.size() != 1) 790897967deSJason M. Bills { 791897967deSJason M. Bills messages::internalError(asyncResp->res); 792897967deSJason M. Bills return; 793897967deSJason M. Bills } 794897967deSJason M. Bills const std::string &targetID = params[0]; 795897967deSJason M. Bills 796897967deSJason M. Bills // Go through the log files and check the unique ID for each entry to 797897967deSJason M. Bills // find the target entry 798897967deSJason M. Bills std::vector<std::filesystem::path> redfishLogFiles; 799897967deSJason M. Bills getRedfishLogFiles(redfishLogFiles); 800897967deSJason M. Bills std::string logEntry; 801897967deSJason M. Bills 802897967deSJason M. Bills // Oldest logs are in the last file, so start there and loop backwards 803897967deSJason M. Bills for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); 804897967deSJason M. Bills it++) 805897967deSJason M. Bills { 806897967deSJason M. Bills std::ifstream logStream(*it); 807897967deSJason M. Bills if (!logStream.is_open()) 808897967deSJason M. Bills { 809897967deSJason M. Bills continue; 810897967deSJason M. Bills } 811897967deSJason M. Bills 812897967deSJason M. Bills // Reset the unique ID on the first entry 813897967deSJason M. Bills bool firstEntry = true; 814897967deSJason M. Bills while (std::getline(logStream, logEntry)) 815897967deSJason M. Bills { 816897967deSJason M. Bills std::string idStr; 817897967deSJason M. Bills if (!getUniqueEntryID(logEntry, idStr, firstEntry)) 818897967deSJason M. Bills { 819897967deSJason M. Bills continue; 820897967deSJason M. Bills } 821897967deSJason M. Bills 822897967deSJason M. Bills if (firstEntry) 823897967deSJason M. Bills { 824897967deSJason M. Bills firstEntry = false; 825897967deSJason M. Bills } 826897967deSJason M. Bills 827897967deSJason M. Bills if (idStr == targetID) 828897967deSJason M. Bills { 829897967deSJason M. Bills if (fillEventLogEntryJson(idStr, logEntry, 830897967deSJason M. Bills asyncResp->res.jsonValue) != 0) 831897967deSJason M. Bills { 832897967deSJason M. Bills messages::internalError(asyncResp->res); 833897967deSJason M. Bills return; 834897967deSJason M. Bills } 835897967deSJason M. Bills return; 836897967deSJason M. Bills } 837897967deSJason M. Bills } 838897967deSJason M. Bills } 839897967deSJason M. Bills // Requested ID was not found 840897967deSJason M. Bills messages::resourceMissingAtURI(asyncResp->res, targetID); 841897967deSJason M. Bills } 842897967deSJason M. Bills }; 843897967deSJason M. Bills 84408a4e4b5SAnthony Wilson class DBusEventLogEntryCollection : public Node 84508a4e4b5SAnthony Wilson { 84608a4e4b5SAnthony Wilson public: 84708a4e4b5SAnthony Wilson template <typename CrowApp> 84808a4e4b5SAnthony Wilson DBusEventLogEntryCollection(CrowApp &app) : 84908a4e4b5SAnthony Wilson Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/") 85008a4e4b5SAnthony Wilson { 85108a4e4b5SAnthony Wilson entityPrivileges = { 85208a4e4b5SAnthony Wilson {boost::beast::http::verb::get, {{"Login"}}}, 85308a4e4b5SAnthony Wilson {boost::beast::http::verb::head, {{"Login"}}}, 85408a4e4b5SAnthony Wilson {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 85508a4e4b5SAnthony Wilson {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 85608a4e4b5SAnthony Wilson {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 85708a4e4b5SAnthony Wilson {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 85808a4e4b5SAnthony Wilson } 85908a4e4b5SAnthony Wilson 86008a4e4b5SAnthony Wilson private: 86108a4e4b5SAnthony Wilson void doGet(crow::Response &res, const crow::Request &req, 86208a4e4b5SAnthony Wilson const std::vector<std::string> ¶ms) override 86308a4e4b5SAnthony Wilson { 86408a4e4b5SAnthony Wilson std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 86508a4e4b5SAnthony Wilson 86608a4e4b5SAnthony Wilson // Collections don't include the static data added by SubRoute because 86708a4e4b5SAnthony Wilson // it has a duplicate entry for members 86808a4e4b5SAnthony Wilson asyncResp->res.jsonValue["@odata.type"] = 86908a4e4b5SAnthony Wilson "#LogEntryCollection.LogEntryCollection"; 87008a4e4b5SAnthony Wilson asyncResp->res.jsonValue["@odata.context"] = 87108a4e4b5SAnthony Wilson "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection"; 87208a4e4b5SAnthony Wilson asyncResp->res.jsonValue["@odata.id"] = 87308a4e4b5SAnthony Wilson "/redfish/v1/Systems/system/LogServices/EventLog/Entries"; 87408a4e4b5SAnthony Wilson asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 87508a4e4b5SAnthony Wilson asyncResp->res.jsonValue["Description"] = 87608a4e4b5SAnthony Wilson "Collection of System Event Log Entries"; 87708a4e4b5SAnthony Wilson 878cb92c03bSAndrew Geissler // DBus implementation of EventLog/Entries 879cb92c03bSAndrew Geissler // Make call to Logging Service to find all log entry objects 880cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 881cb92c03bSAndrew Geissler [asyncResp](const boost::system::error_code ec, 882cb92c03bSAndrew Geissler GetManagedObjectsType &resp) { 883cb92c03bSAndrew Geissler if (ec) 884cb92c03bSAndrew Geissler { 885cb92c03bSAndrew Geissler // TODO Handle for specific error code 886cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR 887cb92c03bSAndrew Geissler << "getLogEntriesIfaceData resp_handler got error " 888cb92c03bSAndrew Geissler << ec; 889cb92c03bSAndrew Geissler messages::internalError(asyncResp->res); 890cb92c03bSAndrew Geissler return; 891cb92c03bSAndrew Geissler } 892cb92c03bSAndrew Geissler nlohmann::json &entriesArray = 893cb92c03bSAndrew Geissler asyncResp->res.jsonValue["Members"]; 894cb92c03bSAndrew Geissler entriesArray = nlohmann::json::array(); 895cb92c03bSAndrew Geissler for (auto &objectPath : resp) 896cb92c03bSAndrew Geissler { 897cb92c03bSAndrew Geissler for (auto &interfaceMap : objectPath.second) 898cb92c03bSAndrew Geissler { 899cb92c03bSAndrew Geissler if (interfaceMap.first != 900cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging.Entry") 901cb92c03bSAndrew Geissler { 902cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "Bailing early on " 903cb92c03bSAndrew Geissler << interfaceMap.first; 904cb92c03bSAndrew Geissler continue; 905cb92c03bSAndrew Geissler } 906cb92c03bSAndrew Geissler entriesArray.push_back({}); 907cb92c03bSAndrew Geissler nlohmann::json &thisEntry = entriesArray.back(); 90866664f25SEd Tanous uint32_t *id = nullptr; 90966664f25SEd Tanous std::time_t timestamp{}; 91066664f25SEd Tanous std::string *severity = nullptr; 91166664f25SEd Tanous std::string *message = nullptr; 912cb92c03bSAndrew Geissler for (auto &propertyMap : interfaceMap.second) 913cb92c03bSAndrew Geissler { 914cb92c03bSAndrew Geissler if (propertyMap.first == "Id") 915cb92c03bSAndrew Geissler { 916cb92c03bSAndrew Geissler id = sdbusplus::message::variant_ns::get_if< 917cb92c03bSAndrew Geissler uint32_t>(&propertyMap.second); 918cb92c03bSAndrew Geissler if (id == nullptr) 919cb92c03bSAndrew Geissler { 920cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 921cb92c03bSAndrew Geissler "Id"); 922cb92c03bSAndrew Geissler } 923cb92c03bSAndrew Geissler } 924cb92c03bSAndrew Geissler else if (propertyMap.first == "Timestamp") 925cb92c03bSAndrew Geissler { 926cb92c03bSAndrew Geissler const uint64_t *millisTimeStamp = 927cb92c03bSAndrew Geissler std::get_if<uint64_t>(&propertyMap.second); 928cb92c03bSAndrew Geissler if (millisTimeStamp == nullptr) 929cb92c03bSAndrew Geissler { 930cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 931cb92c03bSAndrew Geissler "Timestamp"); 932271584abSEd Tanous continue; 933cb92c03bSAndrew Geissler } 934cb92c03bSAndrew Geissler // Retrieve Created property with format: 935cb92c03bSAndrew Geissler // yyyy-mm-ddThh:mm:ss 936cb92c03bSAndrew Geissler std::chrono::milliseconds chronoTimeStamp( 937cb92c03bSAndrew Geissler *millisTimeStamp); 938271584abSEd Tanous timestamp = std::chrono::duration_cast< 939271584abSEd Tanous std::chrono::duration<int>>( 940271584abSEd Tanous chronoTimeStamp) 941cb92c03bSAndrew Geissler .count(); 942cb92c03bSAndrew Geissler } 943cb92c03bSAndrew Geissler else if (propertyMap.first == "Severity") 944cb92c03bSAndrew Geissler { 945cb92c03bSAndrew Geissler severity = std::get_if<std::string>( 946cb92c03bSAndrew Geissler &propertyMap.second); 947cb92c03bSAndrew Geissler if (severity == nullptr) 948cb92c03bSAndrew Geissler { 949cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 950cb92c03bSAndrew Geissler "Severity"); 951cb92c03bSAndrew Geissler } 952cb92c03bSAndrew Geissler } 953cb92c03bSAndrew Geissler else if (propertyMap.first == "Message") 954cb92c03bSAndrew Geissler { 955cb92c03bSAndrew Geissler message = std::get_if<std::string>( 956cb92c03bSAndrew Geissler &propertyMap.second); 957cb92c03bSAndrew Geissler if (message == nullptr) 958cb92c03bSAndrew Geissler { 959cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 960cb92c03bSAndrew Geissler "Message"); 961cb92c03bSAndrew Geissler } 962cb92c03bSAndrew Geissler } 963cb92c03bSAndrew Geissler } 964cb92c03bSAndrew Geissler thisEntry = { 965cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 966cb92c03bSAndrew Geissler {"@odata.context", "/redfish/v1/" 967cb92c03bSAndrew Geissler "$metadata#LogEntry.LogEntry"}, 968cb92c03bSAndrew Geissler {"@odata.id", 969cb92c03bSAndrew Geissler "/redfish/v1/Systems/system/LogServices/EventLog/" 970cb92c03bSAndrew Geissler "Entries/" + 971cb92c03bSAndrew Geissler std::to_string(*id)}, 97227062605SAnthony Wilson {"Name", "System Event Log Entry"}, 973cb92c03bSAndrew Geissler {"Id", std::to_string(*id)}, 974cb92c03bSAndrew Geissler {"Message", *message}, 975cb92c03bSAndrew Geissler {"EntryType", "Event"}, 976cb92c03bSAndrew Geissler {"Severity", 977cb92c03bSAndrew Geissler translateSeverityDbusToRedfish(*severity)}, 978cb92c03bSAndrew Geissler {"Created", crow::utility::getDateTime(timestamp)}}; 979cb92c03bSAndrew Geissler } 980cb92c03bSAndrew Geissler } 981cb92c03bSAndrew Geissler std::sort(entriesArray.begin(), entriesArray.end(), 982cb92c03bSAndrew Geissler [](const nlohmann::json &left, 983cb92c03bSAndrew Geissler const nlohmann::json &right) { 984cb92c03bSAndrew Geissler return (left["Id"] <= right["Id"]); 985cb92c03bSAndrew Geissler }); 986cb92c03bSAndrew Geissler asyncResp->res.jsonValue["Members@odata.count"] = 987cb92c03bSAndrew Geissler entriesArray.size(); 988cb92c03bSAndrew Geissler }, 989cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging", 990cb92c03bSAndrew Geissler "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 991c4bf6374SJason M. Bills } 992c4bf6374SJason M. Bills }; 993c4bf6374SJason M. Bills 99408a4e4b5SAnthony Wilson class DBusEventLogEntry : public Node 995c4bf6374SJason M. Bills { 996c4bf6374SJason M. Bills public: 99708a4e4b5SAnthony Wilson DBusEventLogEntry(CrowApp &app) : 998c4bf6374SJason M. Bills Node(app, 999029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/", 1000029573d4SEd Tanous std::string()) 1001c4bf6374SJason M. Bills { 1002c4bf6374SJason M. Bills entityPrivileges = { 1003c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1004c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1005c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1006c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1007c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1008c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1009c4bf6374SJason M. Bills } 1010c4bf6374SJason M. Bills 1011c4bf6374SJason M. Bills private: 1012c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1013c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 1014c4bf6374SJason M. Bills { 1015c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1016029573d4SEd Tanous if (params.size() != 1) 1017c4bf6374SJason M. Bills { 1018c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 1019c4bf6374SJason M. Bills return; 1020c4bf6374SJason M. Bills } 1021029573d4SEd Tanous const std::string &entryID = params[0]; 1022cb92c03bSAndrew Geissler 1023cb92c03bSAndrew Geissler // DBus implementation of EventLog/Entries 1024cb92c03bSAndrew Geissler // Make call to Logging Service to find all log entry objects 1025cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 1026cb92c03bSAndrew Geissler [asyncResp, entryID](const boost::system::error_code ec, 1027cb92c03bSAndrew Geissler GetManagedPropertyType &resp) { 1028cb92c03bSAndrew Geissler if (ec) 1029cb92c03bSAndrew Geissler { 1030cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR 1031cb92c03bSAndrew Geissler << "EventLogEntry (DBus) resp_handler got error " << ec; 1032cb92c03bSAndrew Geissler messages::internalError(asyncResp->res); 1033cb92c03bSAndrew Geissler return; 1034cb92c03bSAndrew Geissler } 103566664f25SEd Tanous uint32_t *id = nullptr; 103666664f25SEd Tanous std::time_t timestamp{}; 103766664f25SEd Tanous std::string *severity = nullptr; 103866664f25SEd Tanous std::string *message = nullptr; 1039cb92c03bSAndrew Geissler for (auto &propertyMap : resp) 1040cb92c03bSAndrew Geissler { 1041cb92c03bSAndrew Geissler if (propertyMap.first == "Id") 1042cb92c03bSAndrew Geissler { 1043cb92c03bSAndrew Geissler id = std::get_if<uint32_t>(&propertyMap.second); 1044cb92c03bSAndrew Geissler if (id == nullptr) 1045cb92c03bSAndrew Geissler { 1046cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, "Id"); 1047cb92c03bSAndrew Geissler } 1048cb92c03bSAndrew Geissler } 1049cb92c03bSAndrew Geissler else if (propertyMap.first == "Timestamp") 1050cb92c03bSAndrew Geissler { 1051cb92c03bSAndrew Geissler const uint64_t *millisTimeStamp = 1052cb92c03bSAndrew Geissler std::get_if<uint64_t>(&propertyMap.second); 1053cb92c03bSAndrew Geissler if (millisTimeStamp == nullptr) 1054cb92c03bSAndrew Geissler { 1055cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 1056cb92c03bSAndrew Geissler "Timestamp"); 1057271584abSEd Tanous continue; 1058cb92c03bSAndrew Geissler } 1059cb92c03bSAndrew Geissler // Retrieve Created property with format: 1060cb92c03bSAndrew Geissler // yyyy-mm-ddThh:mm:ss 1061cb92c03bSAndrew Geissler std::chrono::milliseconds chronoTimeStamp( 1062cb92c03bSAndrew Geissler *millisTimeStamp); 1063cb92c03bSAndrew Geissler timestamp = 1064271584abSEd Tanous std::chrono::duration_cast< 1065271584abSEd Tanous std::chrono::duration<int>>(chronoTimeStamp) 1066cb92c03bSAndrew Geissler .count(); 1067cb92c03bSAndrew Geissler } 1068cb92c03bSAndrew Geissler else if (propertyMap.first == "Severity") 1069cb92c03bSAndrew Geissler { 1070cb92c03bSAndrew Geissler severity = 1071cb92c03bSAndrew Geissler std::get_if<std::string>(&propertyMap.second); 1072cb92c03bSAndrew Geissler if (severity == nullptr) 1073cb92c03bSAndrew Geissler { 1074cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 1075cb92c03bSAndrew Geissler "Severity"); 1076cb92c03bSAndrew Geissler } 1077cb92c03bSAndrew Geissler } 1078cb92c03bSAndrew Geissler else if (propertyMap.first == "Message") 1079cb92c03bSAndrew Geissler { 1080cb92c03bSAndrew Geissler message = std::get_if<std::string>(&propertyMap.second); 1081cb92c03bSAndrew Geissler if (message == nullptr) 1082cb92c03bSAndrew Geissler { 1083cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 1084cb92c03bSAndrew Geissler "Message"); 1085cb92c03bSAndrew Geissler } 1086cb92c03bSAndrew Geissler } 1087cb92c03bSAndrew Geissler } 1088271584abSEd Tanous if (id == nullptr || message == nullptr || severity == nullptr) 1089271584abSEd Tanous { 1090271584abSEd Tanous return; 1091271584abSEd Tanous } 1092cb92c03bSAndrew Geissler asyncResp->res.jsonValue = { 1093cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 1094cb92c03bSAndrew Geissler {"@odata.context", "/redfish/v1/" 1095cb92c03bSAndrew Geissler "$metadata#LogEntry.LogEntry"}, 1096cb92c03bSAndrew Geissler {"@odata.id", 1097cb92c03bSAndrew Geissler "/redfish/v1/Systems/system/LogServices/EventLog/" 1098cb92c03bSAndrew Geissler "Entries/" + 1099cb92c03bSAndrew Geissler std::to_string(*id)}, 110027062605SAnthony Wilson {"Name", "System Event Log Entry"}, 1101cb92c03bSAndrew Geissler {"Id", std::to_string(*id)}, 1102cb92c03bSAndrew Geissler {"Message", *message}, 1103cb92c03bSAndrew Geissler {"EntryType", "Event"}, 1104cb92c03bSAndrew Geissler {"Severity", translateSeverityDbusToRedfish(*severity)}, 110508a4e4b5SAnthony Wilson {"Created", crow::utility::getDateTime(timestamp)}}; 1106cb92c03bSAndrew Geissler }, 1107cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging", 1108cb92c03bSAndrew Geissler "/xyz/openbmc_project/logging/entry/" + entryID, 1109cb92c03bSAndrew Geissler "org.freedesktop.DBus.Properties", "GetAll", 1110cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging.Entry"); 1111c4bf6374SJason M. Bills } 1112336e96c6SChicago Duan 1113336e96c6SChicago Duan void doDelete(crow::Response &res, const crow::Request &req, 1114336e96c6SChicago Duan const std::vector<std::string> ¶ms) override 1115336e96c6SChicago Duan { 1116336e96c6SChicago Duan 1117336e96c6SChicago Duan BMCWEB_LOG_DEBUG << "Do delete single event entries."; 1118336e96c6SChicago Duan 1119336e96c6SChicago Duan auto asyncResp = std::make_shared<AsyncResp>(res); 1120336e96c6SChicago Duan 1121336e96c6SChicago Duan if (params.size() != 1) 1122336e96c6SChicago Duan { 1123336e96c6SChicago Duan messages::internalError(asyncResp->res); 1124336e96c6SChicago Duan return; 1125336e96c6SChicago Duan } 1126336e96c6SChicago Duan std::string entryID = params[0]; 1127336e96c6SChicago Duan 1128336e96c6SChicago Duan dbus::utility::escapePathForDbus(entryID); 1129336e96c6SChicago Duan 1130336e96c6SChicago Duan // Process response from Logging service. 1131336e96c6SChicago Duan auto respHandler = [asyncResp](const boost::system::error_code ec) { 1132336e96c6SChicago Duan BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done"; 1133336e96c6SChicago Duan if (ec) 1134336e96c6SChicago Duan { 1135336e96c6SChicago Duan // TODO Handle for specific error code 1136336e96c6SChicago Duan BMCWEB_LOG_ERROR 1137336e96c6SChicago Duan << "EventLogEntry (DBus) doDelete respHandler got error " 1138336e96c6SChicago Duan << ec; 1139336e96c6SChicago Duan asyncResp->res.result( 1140336e96c6SChicago Duan boost::beast::http::status::internal_server_error); 1141336e96c6SChicago Duan return; 1142336e96c6SChicago Duan } 1143336e96c6SChicago Duan 1144336e96c6SChicago Duan asyncResp->res.result(boost::beast::http::status::ok); 1145336e96c6SChicago Duan }; 1146336e96c6SChicago Duan 1147336e96c6SChicago Duan // Make call to Logging service to request Delete Log 1148336e96c6SChicago Duan crow::connections::systemBus->async_method_call( 1149336e96c6SChicago Duan respHandler, "xyz.openbmc_project.Logging", 1150336e96c6SChicago Duan "/xyz/openbmc_project/logging/entry/" + entryID, 1151336e96c6SChicago Duan "xyz.openbmc_project.Object.Delete", "Delete"); 1152336e96c6SChicago Duan } 1153c4bf6374SJason M. Bills }; 1154c4bf6374SJason M. Bills 1155c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node 1156c4bf6374SJason M. Bills { 1157c4bf6374SJason M. Bills public: 1158c4bf6374SJason M. Bills template <typename CrowApp> 1159c4bf6374SJason M. Bills BMCLogServiceCollection(CrowApp &app) : 11604ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/") 11611da66f75SEd Tanous { 11621da66f75SEd Tanous entityPrivileges = { 1163e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1164e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1165e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1166e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1167e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1168e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 11691da66f75SEd Tanous } 11701da66f75SEd Tanous 11711da66f75SEd Tanous private: 11721da66f75SEd Tanous /** 11731da66f75SEd Tanous * Functions triggers appropriate requests on DBus 11741da66f75SEd Tanous */ 11751da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 11761da66f75SEd Tanous const std::vector<std::string> ¶ms) override 11771da66f75SEd Tanous { 1178e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 11791da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 11801da66f75SEd Tanous // it has a duplicate entry for members 1181e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 11821da66f75SEd Tanous "#LogServiceCollection.LogServiceCollection"; 1183e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1184c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection"; 1185e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 1186e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices"; 1187e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection"; 1188e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 11891da66f75SEd Tanous "Collection of LogServices for this Manager"; 1190c4bf6374SJason M. Bills nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; 1191c4bf6374SJason M. Bills logServiceArray = nlohmann::json::array(); 1192c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL 1193c4bf6374SJason M. Bills logServiceArray.push_back( 119408a4e4b5SAnthony Wilson {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}}); 1195c4bf6374SJason M. Bills #endif 1196e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 1197c4bf6374SJason M. Bills logServiceArray.size(); 11981da66f75SEd Tanous } 11991da66f75SEd Tanous }; 12001da66f75SEd Tanous 1201c4bf6374SJason M. Bills class BMCJournalLogService : public Node 12021da66f75SEd Tanous { 12031da66f75SEd Tanous public: 12041da66f75SEd Tanous template <typename CrowApp> 1205c4bf6374SJason M. Bills BMCJournalLogService(CrowApp &app) : 1206c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/") 1207e1f26343SJason M. Bills { 1208e1f26343SJason M. Bills entityPrivileges = { 1209e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1210e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1211e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1212e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1213e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1214e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1215e1f26343SJason M. Bills } 1216e1f26343SJason M. Bills 1217e1f26343SJason M. Bills private: 1218e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1219e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1220e1f26343SJason M. Bills { 1221e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1222e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1223e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 12240f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 12250f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal"; 1226e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1227e1f26343SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 1228c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service"; 1229c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service"; 1230c4bf6374SJason M. Bills asyncResp->res.jsonValue["Id"] = "BMC Journal"; 1231e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 1232cd50aa42SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 1233cd50aa42SJason M. Bills {"@odata.id", 1234086be238SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}}; 1235e1f26343SJason M. Bills } 1236e1f26343SJason M. Bills }; 1237e1f26343SJason M. Bills 1238c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID, 1239e1f26343SJason M. Bills sd_journal *journal, 1240c4bf6374SJason M. Bills nlohmann::json &bmcJournalLogEntryJson) 1241e1f26343SJason M. Bills { 1242e1f26343SJason M. Bills // Get the Log Entry contents 1243e1f26343SJason M. Bills int ret = 0; 1244e1f26343SJason M. Bills 124539e77504SEd Tanous std::string_view msg; 124616428a1aSJason M. Bills ret = getJournalMetadata(journal, "MESSAGE", msg); 1247e1f26343SJason M. Bills if (ret < 0) 1248e1f26343SJason M. Bills { 1249e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret); 1250e1f26343SJason M. Bills return 1; 1251e1f26343SJason M. Bills } 1252e1f26343SJason M. Bills 1253e1f26343SJason M. Bills // Get the severity from the PRIORITY field 1254271584abSEd Tanous long int severity = 8; // Default to an invalid priority 125516428a1aSJason M. Bills ret = getJournalMetadata(journal, "PRIORITY", 10, severity); 1256e1f26343SJason M. Bills if (ret < 0) 1257e1f26343SJason M. Bills { 1258e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret); 1259e1f26343SJason M. Bills } 1260e1f26343SJason M. Bills 1261e1f26343SJason M. Bills // Get the Created time from the timestamp 126216428a1aSJason M. Bills std::string entryTimeStr; 126316428a1aSJason M. Bills if (!getEntryTimestamp(journal, entryTimeStr)) 1264e1f26343SJason M. Bills { 126516428a1aSJason M. Bills return 1; 1266e1f26343SJason M. Bills } 1267e1f26343SJason M. Bills 1268e1f26343SJason M. Bills // Fill in the log entry with the gathered data 1269c4bf6374SJason M. Bills bmcJournalLogEntryJson = { 1270cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 1271e1f26343SJason M. Bills {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 1272c4bf6374SJason M. Bills {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" + 1273c4bf6374SJason M. Bills bmcJournalLogEntryID}, 1274e1f26343SJason M. Bills {"Name", "BMC Journal Entry"}, 1275c4bf6374SJason M. Bills {"Id", bmcJournalLogEntryID}, 127616428a1aSJason M. Bills {"Message", msg}, 1277e1f26343SJason M. Bills {"EntryType", "Oem"}, 1278e1f26343SJason M. Bills {"Severity", 1279b6a61a5eSJason M. Bills severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"}, 1280086be238SEd Tanous {"OemRecordFormat", "BMC Journal Entry"}, 1281e1f26343SJason M. Bills {"Created", std::move(entryTimeStr)}}; 1282e1f26343SJason M. Bills return 0; 1283e1f26343SJason M. Bills } 1284e1f26343SJason M. Bills 1285c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node 1286e1f26343SJason M. Bills { 1287e1f26343SJason M. Bills public: 1288e1f26343SJason M. Bills template <typename CrowApp> 1289c4bf6374SJason M. Bills BMCJournalLogEntryCollection(CrowApp &app) : 1290c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/") 1291e1f26343SJason M. Bills { 1292e1f26343SJason M. Bills entityPrivileges = { 1293e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1294e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1295e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1296e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1297e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1298e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1299e1f26343SJason M. Bills } 1300e1f26343SJason M. Bills 1301e1f26343SJason M. Bills private: 1302e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1303e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1304e1f26343SJason M. Bills { 1305e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1306193ad2faSJason M. Bills static constexpr const long maxEntriesPerPage = 1000; 1307271584abSEd Tanous uint64_t skip = 0; 1308271584abSEd Tanous uint64_t top = maxEntriesPerPage; // Show max entries by default 130916428a1aSJason M. Bills if (!getSkipParam(asyncResp->res, req, skip)) 1310193ad2faSJason M. Bills { 1311193ad2faSJason M. Bills return; 1312193ad2faSJason M. Bills } 131316428a1aSJason M. Bills if (!getTopParam(asyncResp->res, req, top)) 1314193ad2faSJason M. Bills { 1315193ad2faSJason M. Bills return; 1316193ad2faSJason M. Bills } 1317e1f26343SJason M. Bills // Collections don't include the static data added by SubRoute because 1318e1f26343SJason M. Bills // it has a duplicate entry for members 1319e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1320e1f26343SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 13210f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 13220f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; 1323e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1324c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection"; 1325e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 1326c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; 1327e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; 1328e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 1329e1f26343SJason M. Bills "Collection of BMC Journal Entries"; 13300f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 13310f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; 1332e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 1333e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 1334e1f26343SJason M. Bills 1335e1f26343SJason M. Bills // Go through the journal and use the timestamp to create a unique ID 1336e1f26343SJason M. Bills // for each entry 1337e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 1338e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 1339e1f26343SJason M. Bills if (ret < 0) 1340e1f26343SJason M. Bills { 1341e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 1342f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1343e1f26343SJason M. Bills return; 1344e1f26343SJason M. Bills } 1345e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 1346e1f26343SJason M. Bills journalTmp, sd_journal_close); 1347e1f26343SJason M. Bills journalTmp = nullptr; 1348b01bf299SEd Tanous uint64_t entryCount = 0; 1349e85d6b16SJason M. Bills // Reset the unique ID on the first entry 1350e85d6b16SJason M. Bills bool firstEntry = true; 1351e1f26343SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 1352e1f26343SJason M. Bills { 1353193ad2faSJason M. Bills entryCount++; 1354193ad2faSJason M. Bills // Handle paging using skip (number of entries to skip from the 1355193ad2faSJason M. Bills // start) and top (number of entries to display) 1356193ad2faSJason M. Bills if (entryCount <= skip || entryCount > skip + top) 1357193ad2faSJason M. Bills { 1358193ad2faSJason M. Bills continue; 1359193ad2faSJason M. Bills } 1360193ad2faSJason M. Bills 136116428a1aSJason M. Bills std::string idStr; 1362e85d6b16SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr, firstEntry)) 1363e1f26343SJason M. Bills { 1364e1f26343SJason M. Bills continue; 1365e1f26343SJason M. Bills } 1366e1f26343SJason M. Bills 1367e85d6b16SJason M. Bills if (firstEntry) 1368e85d6b16SJason M. Bills { 1369e85d6b16SJason M. Bills firstEntry = false; 1370e85d6b16SJason M. Bills } 1371e85d6b16SJason M. Bills 1372e1f26343SJason M. Bills logEntryArray.push_back({}); 1373c4bf6374SJason M. Bills nlohmann::json &bmcJournalLogEntry = logEntryArray.back(); 1374c4bf6374SJason M. Bills if (fillBMCJournalLogEntryJson(idStr, journal.get(), 1375c4bf6374SJason M. Bills bmcJournalLogEntry) != 0) 1376e1f26343SJason M. Bills { 1377f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1378e1f26343SJason M. Bills return; 1379e1f26343SJason M. Bills } 1380e1f26343SJason M. Bills } 1381193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 1382193ad2faSJason M. Bills if (skip + top < entryCount) 1383193ad2faSJason M. Bills { 1384193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 1385c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" + 1386193ad2faSJason M. Bills std::to_string(skip + top); 1387193ad2faSJason M. Bills } 1388e1f26343SJason M. Bills } 1389e1f26343SJason M. Bills }; 1390e1f26343SJason M. Bills 1391c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node 1392e1f26343SJason M. Bills { 1393e1f26343SJason M. Bills public: 1394c4bf6374SJason M. Bills BMCJournalLogEntry(CrowApp &app) : 1395c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/", 1396e1f26343SJason M. Bills std::string()) 1397e1f26343SJason M. Bills { 1398e1f26343SJason M. Bills entityPrivileges = { 1399e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1400e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1401e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1402e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1403e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1404e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1405e1f26343SJason M. Bills } 1406e1f26343SJason M. Bills 1407e1f26343SJason M. Bills private: 1408e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1409e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1410e1f26343SJason M. Bills { 1411e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1412e1f26343SJason M. Bills if (params.size() != 1) 1413e1f26343SJason M. Bills { 1414f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1415e1f26343SJason M. Bills return; 1416e1f26343SJason M. Bills } 141716428a1aSJason M. Bills const std::string &entryID = params[0]; 1418e1f26343SJason M. Bills // Convert the unique ID back to a timestamp to find the entry 1419e1f26343SJason M. Bills uint64_t ts = 0; 1420271584abSEd Tanous uint64_t index = 0; 142116428a1aSJason M. Bills if (!getTimestampFromID(asyncResp->res, entryID, ts, index)) 1422e1f26343SJason M. Bills { 142316428a1aSJason M. Bills return; 1424e1f26343SJason M. Bills } 1425e1f26343SJason M. Bills 1426e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 1427e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 1428e1f26343SJason M. Bills if (ret < 0) 1429e1f26343SJason M. Bills { 1430e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 1431f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1432e1f26343SJason M. Bills return; 1433e1f26343SJason M. Bills } 1434e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 1435e1f26343SJason M. Bills journalTmp, sd_journal_close); 1436e1f26343SJason M. Bills journalTmp = nullptr; 1437e1f26343SJason M. Bills // Go to the timestamp in the log and move to the entry at the index 1438af07e3f5SJason M. Bills // tracking the unique ID 1439af07e3f5SJason M. Bills std::string idStr; 1440af07e3f5SJason M. Bills bool firstEntry = true; 1441e1f26343SJason M. Bills ret = sd_journal_seek_realtime_usec(journal.get(), ts); 1442271584abSEd Tanous for (uint64_t i = 0; i <= index; i++) 1443e1f26343SJason M. Bills { 1444e1f26343SJason M. Bills sd_journal_next(journal.get()); 1445af07e3f5SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr, firstEntry)) 1446af07e3f5SJason M. Bills { 1447af07e3f5SJason M. Bills messages::internalError(asyncResp->res); 1448af07e3f5SJason M. Bills return; 1449af07e3f5SJason M. Bills } 1450af07e3f5SJason M. Bills if (firstEntry) 1451af07e3f5SJason M. Bills { 1452af07e3f5SJason M. Bills firstEntry = false; 1453af07e3f5SJason M. Bills } 1454e1f26343SJason M. Bills } 1455c4bf6374SJason M. Bills // Confirm that the entry ID matches what was requested 1456af07e3f5SJason M. Bills if (idStr != entryID) 1457c4bf6374SJason M. Bills { 1458c4bf6374SJason M. Bills messages::resourceMissingAtURI(asyncResp->res, entryID); 1459c4bf6374SJason M. Bills return; 1460c4bf6374SJason M. Bills } 1461c4bf6374SJason M. Bills 1462c4bf6374SJason M. Bills if (fillBMCJournalLogEntryJson(entryID, journal.get(), 1463e1f26343SJason M. Bills asyncResp->res.jsonValue) != 0) 1464e1f26343SJason M. Bills { 1465f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1466e1f26343SJason M. Bills return; 1467e1f26343SJason M. Bills } 1468e1f26343SJason M. Bills } 1469e1f26343SJason M. Bills }; 1470e1f26343SJason M. Bills 1471424c4176SJason M. Bills class CrashdumpService : public Node 1472e1f26343SJason M. Bills { 1473e1f26343SJason M. Bills public: 1474e1f26343SJason M. Bills template <typename CrowApp> 1475424c4176SJason M. Bills CrashdumpService(CrowApp &app) : 1476424c4176SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/") 14771da66f75SEd Tanous { 14781da66f75SEd Tanous entityPrivileges = { 1479e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1480e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1481e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1482e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1483e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1484e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 14851da66f75SEd Tanous } 14861da66f75SEd Tanous 14871da66f75SEd Tanous private: 14881da66f75SEd Tanous /** 14891da66f75SEd Tanous * Functions triggers appropriate requests on DBus 14901da66f75SEd Tanous */ 14911da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 14921da66f75SEd Tanous const std::vector<std::string> ¶ms) override 14931da66f75SEd Tanous { 1494e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 14951da66f75SEd Tanous // Copy over the static data to include the entries added by SubRoute 14960f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 1497424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump"; 1498e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1499e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 1500e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1501c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 1502424c4176SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Service"; 1503424c4176SJason M. Bills asyncResp->res.jsonValue["Description"] = "Crashdump Service"; 1504424c4176SJason M. Bills asyncResp->res.jsonValue["Id"] = "Crashdump"; 1505e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 1506e1f26343SJason M. Bills asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 1507cd50aa42SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 1508cd50aa42SJason M. Bills {"@odata.id", 1509424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}}; 1510e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"] = { 15115b61b5e8SJason M. Bills {"#LogService.ClearLog", 15125b61b5e8SJason M. Bills {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/" 15135b61b5e8SJason M. Bills "Actions/LogService.ClearLog"}}}, 15141da66f75SEd Tanous {"Oem", 1515424c4176SJason M. Bills {{"#Crashdump.OnDemand", 1516424c4176SJason M. Bills {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/" 1517424c4176SJason M. Bills "Actions/Oem/Crashdump.OnDemand"}}}}}}; 15181da66f75SEd Tanous 15191da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI 1520e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"]["Oem"].push_back( 1521424c4176SJason M. Bills {"#Crashdump.SendRawPeci", 152208a4e4b5SAnthony Wilson {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/" 1523424c4176SJason M. Bills "Actions/Oem/Crashdump.SendRawPeci"}}}); 15241da66f75SEd Tanous #endif 15251da66f75SEd Tanous } 15261da66f75SEd Tanous }; 15271da66f75SEd Tanous 15285b61b5e8SJason M. Bills class CrashdumpClear : public Node 15295b61b5e8SJason M. Bills { 15305b61b5e8SJason M. Bills public: 15315b61b5e8SJason M. Bills CrashdumpClear(CrowApp &app) : 15325b61b5e8SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/" 15335b61b5e8SJason M. Bills "LogService.ClearLog/") 15345b61b5e8SJason M. Bills { 15355b61b5e8SJason M. Bills entityPrivileges = { 15365b61b5e8SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 15375b61b5e8SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 15385b61b5e8SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 15395b61b5e8SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 15405b61b5e8SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 15415b61b5e8SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 15425b61b5e8SJason M. Bills } 15435b61b5e8SJason M. Bills 15445b61b5e8SJason M. Bills private: 15455b61b5e8SJason M. Bills void doPost(crow::Response &res, const crow::Request &req, 15465b61b5e8SJason M. Bills const std::vector<std::string> ¶ms) override 15475b61b5e8SJason M. Bills { 15485b61b5e8SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 15495b61b5e8SJason M. Bills 15505b61b5e8SJason M. Bills crow::connections::systemBus->async_method_call( 15515b61b5e8SJason M. Bills [asyncResp](const boost::system::error_code ec, 15525b61b5e8SJason M. Bills const std::string &resp) { 15535b61b5e8SJason M. Bills if (ec) 15545b61b5e8SJason M. Bills { 15555b61b5e8SJason M. Bills messages::internalError(asyncResp->res); 15565b61b5e8SJason M. Bills return; 15575b61b5e8SJason M. Bills } 15585b61b5e8SJason M. Bills messages::success(asyncResp->res); 15595b61b5e8SJason M. Bills }, 15605b61b5e8SJason M. Bills crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll"); 15615b61b5e8SJason M. Bills } 15625b61b5e8SJason M. Bills }; 15635b61b5e8SJason M. Bills 1564e855dd28SJason M. Bills std::string getLogCreatedTime(const std::string &crashdump) 1565e855dd28SJason M. Bills { 1566e855dd28SJason M. Bills nlohmann::json crashdumpJson = 1567e855dd28SJason M. Bills nlohmann::json::parse(crashdump, nullptr, false); 1568e855dd28SJason M. Bills if (crashdumpJson.is_discarded()) 1569e855dd28SJason M. Bills { 1570e855dd28SJason M. Bills return std::string(); 1571e855dd28SJason M. Bills } 1572e855dd28SJason M. Bills 1573e855dd28SJason M. Bills nlohmann::json::const_iterator cdIt = crashdumpJson.find("crash_data"); 1574e855dd28SJason M. Bills if (cdIt == crashdumpJson.end()) 1575e855dd28SJason M. Bills { 1576e855dd28SJason M. Bills return std::string(); 1577e855dd28SJason M. Bills } 1578e855dd28SJason M. Bills 1579e855dd28SJason M. Bills nlohmann::json::const_iterator siIt = cdIt->find("METADATA"); 1580e855dd28SJason M. Bills if (siIt == cdIt->end()) 1581e855dd28SJason M. Bills { 1582e855dd28SJason M. Bills return std::string(); 1583e855dd28SJason M. Bills } 1584e855dd28SJason M. Bills 1585e855dd28SJason M. Bills nlohmann::json::const_iterator tsIt = siIt->find("timestamp"); 1586e855dd28SJason M. Bills if (tsIt == siIt->end()) 1587e855dd28SJason M. Bills { 1588e855dd28SJason M. Bills return std::string(); 1589e855dd28SJason M. Bills } 1590e855dd28SJason M. Bills 1591e855dd28SJason M. Bills const std::string *logTime = tsIt->get_ptr<const std::string *>(); 1592e855dd28SJason M. Bills if (logTime == nullptr) 1593e855dd28SJason M. Bills { 1594e855dd28SJason M. Bills return std::string(); 1595e855dd28SJason M. Bills } 1596e855dd28SJason M. Bills 1597e855dd28SJason M. Bills std::string redfishDateTime = *logTime; 1598e855dd28SJason M. Bills if (redfishDateTime.length() > 2) 1599e855dd28SJason M. Bills { 1600e855dd28SJason M. Bills redfishDateTime.insert(redfishDateTime.end() - 2, ':'); 1601e855dd28SJason M. Bills } 1602e855dd28SJason M. Bills 1603e855dd28SJason M. Bills return redfishDateTime; 1604e855dd28SJason M. Bills } 1605e855dd28SJason M. Bills 1606e855dd28SJason M. Bills std::string getLogFileName(const std::string &logTime) 1607e855dd28SJason M. Bills { 1608e855dd28SJason M. Bills // Set the crashdump file name to "crashdump_<logTime>.json" using the 1609e855dd28SJason M. Bills // created time without the timezone info 1610e855dd28SJason M. Bills std::string fileTime = logTime; 1611e855dd28SJason M. Bills size_t plusPos = fileTime.rfind('+'); 1612e855dd28SJason M. Bills if (plusPos != std::string::npos) 1613e855dd28SJason M. Bills { 1614e855dd28SJason M. Bills fileTime.erase(plusPos); 1615e855dd28SJason M. Bills } 1616e855dd28SJason M. Bills return "crashdump_" + fileTime + ".json"; 1617e855dd28SJason M. Bills } 1618e855dd28SJason M. Bills 1619e855dd28SJason M. Bills static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp, 1620e855dd28SJason M. Bills const std::string &logID, 1621e855dd28SJason M. Bills nlohmann::json &logEntryJson) 1622e855dd28SJason M. Bills { 1623e855dd28SJason M. Bills auto getStoredLogCallback = [asyncResp, logID, &logEntryJson]( 1624e855dd28SJason M. Bills const boost::system::error_code ec, 1625e855dd28SJason M. Bills const std::variant<std::string> &resp) { 1626e855dd28SJason M. Bills if (ec) 1627e855dd28SJason M. Bills { 1628e855dd28SJason M. Bills BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message(); 16291ddcf01aSJason M. Bills if (ec.value() == 16301ddcf01aSJason M. Bills boost::system::linux_error::bad_request_descriptor) 16311ddcf01aSJason M. Bills { 16321ddcf01aSJason M. Bills messages::resourceNotFound(asyncResp->res, "LogEntry", logID); 16331ddcf01aSJason M. Bills } 16341ddcf01aSJason M. Bills else 16351ddcf01aSJason M. Bills { 1636e855dd28SJason M. Bills messages::internalError(asyncResp->res); 16371ddcf01aSJason M. Bills } 1638e855dd28SJason M. Bills return; 1639e855dd28SJason M. Bills } 1640e855dd28SJason M. Bills const std::string *log = std::get_if<std::string>(&resp); 1641e855dd28SJason M. Bills if (log == nullptr) 1642e855dd28SJason M. Bills { 1643e855dd28SJason M. Bills messages::internalError(asyncResp->res); 1644e855dd28SJason M. Bills return; 1645e855dd28SJason M. Bills } 1646e855dd28SJason M. Bills std::string logTime = getLogCreatedTime(*log); 1647e855dd28SJason M. Bills std::string fileName = getLogFileName(logTime); 1648e855dd28SJason M. Bills 1649e855dd28SJason M. Bills logEntryJson = { 1650e855dd28SJason M. Bills {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 1651e855dd28SJason M. Bills {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 1652e855dd28SJason M. Bills {"@odata.id", 1653e855dd28SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + 1654e855dd28SJason M. Bills logID}, 1655e855dd28SJason M. Bills {"Name", "CPU Crashdump"}, 1656e855dd28SJason M. Bills {"Id", logID}, 1657e855dd28SJason M. Bills {"EntryType", "Oem"}, 1658e855dd28SJason M. Bills {"OemRecordFormat", "Crashdump URI"}, 1659e855dd28SJason M. Bills {"Message", 1660e855dd28SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + 1661e855dd28SJason M. Bills logID + "/" + fileName}, 1662e855dd28SJason M. Bills {"Created", std::move(logTime)}}; 1663e855dd28SJason M. Bills }; 1664e855dd28SJason M. Bills crow::connections::systemBus->async_method_call( 16655b61b5e8SJason M. Bills std::move(getStoredLogCallback), crashdumpObject, 16665b61b5e8SJason M. Bills crashdumpPath + std::string("/") + logID, 16675b61b5e8SJason M. Bills "org.freedesktop.DBus.Properties", "Get", crashdumpInterface, "Log"); 1668e855dd28SJason M. Bills } 1669e855dd28SJason M. Bills 1670424c4176SJason M. Bills class CrashdumpEntryCollection : public Node 16711da66f75SEd Tanous { 16721da66f75SEd Tanous public: 16731da66f75SEd Tanous template <typename CrowApp> 1674424c4176SJason M. Bills CrashdumpEntryCollection(CrowApp &app) : 1675424c4176SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/") 16761da66f75SEd Tanous { 16771da66f75SEd Tanous entityPrivileges = { 1678e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1679e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1680e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1681e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1682e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1683e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 16841da66f75SEd Tanous } 16851da66f75SEd Tanous 16861da66f75SEd Tanous private: 16871da66f75SEd Tanous /** 16881da66f75SEd Tanous * Functions triggers appropriate requests on DBus 16891da66f75SEd Tanous */ 16901da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 16911da66f75SEd Tanous const std::vector<std::string> ¶ms) override 16921da66f75SEd Tanous { 1693e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 16941da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 16951da66f75SEd Tanous // it has a duplicate entry for members 1696e1f26343SJason M. Bills auto getLogEntriesCallback = [asyncResp]( 1697e1f26343SJason M. Bills const boost::system::error_code ec, 16981da66f75SEd Tanous const std::vector<std::string> &resp) { 16991da66f75SEd Tanous if (ec) 17001da66f75SEd Tanous { 17011da66f75SEd Tanous if (ec.value() != 17021da66f75SEd Tanous boost::system::errc::no_such_file_or_directory) 17031da66f75SEd Tanous { 17041da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to get entries ec: " 17051da66f75SEd Tanous << ec.message(); 1706f12894f8SJason M. Bills messages::internalError(asyncResp->res); 17071da66f75SEd Tanous return; 17081da66f75SEd Tanous } 17091da66f75SEd Tanous } 1710e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 17111da66f75SEd Tanous "#LogEntryCollection.LogEntryCollection"; 17120f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 1713424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"; 1714e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1715e855dd28SJason M. Bills "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection"; 1716424c4176SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries"; 1717e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 1718424c4176SJason M. Bills "Collection of Crashdump Entries"; 1719e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 1720e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 1721e855dd28SJason M. Bills std::vector<std::string> logIDs; 1722e855dd28SJason M. Bills // Get the list of log entries and build up an empty array big 1723e855dd28SJason M. Bills // enough to hold them 17241da66f75SEd Tanous for (const std::string &objpath : resp) 17251da66f75SEd Tanous { 1726e855dd28SJason M. Bills // Get the log ID 17274ed77cd5SEd Tanous std::size_t lastPos = objpath.rfind("/"); 1728e855dd28SJason M. Bills if (lastPos == std::string::npos) 17291da66f75SEd Tanous { 1730e855dd28SJason M. Bills continue; 17311da66f75SEd Tanous } 1732e855dd28SJason M. Bills logIDs.emplace_back(objpath.substr(lastPos + 1)); 1733e855dd28SJason M. Bills 1734e855dd28SJason M. Bills // Add a space for the log entry to the array 1735e855dd28SJason M. Bills logEntryArray.push_back({}); 1736e855dd28SJason M. Bills } 1737e855dd28SJason M. Bills // Now go through and set up async calls to fill in the entries 1738e855dd28SJason M. Bills size_t index = 0; 1739e855dd28SJason M. Bills for (const std::string &logID : logIDs) 1740e855dd28SJason M. Bills { 1741e855dd28SJason M. Bills // Add the log entry to the array 1742e855dd28SJason M. Bills logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]); 17431da66f75SEd Tanous } 1744e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 1745e1f26343SJason M. Bills logEntryArray.size(); 17461da66f75SEd Tanous }; 17471da66f75SEd Tanous crow::connections::systemBus->async_method_call( 17481da66f75SEd Tanous std::move(getLogEntriesCallback), 17491da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", 17501da66f75SEd Tanous "/xyz/openbmc_project/object_mapper", 17511da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0, 17525b61b5e8SJason M. Bills std::array<const char *, 1>{crashdumpInterface}); 17531da66f75SEd Tanous } 17541da66f75SEd Tanous }; 17551da66f75SEd Tanous 1756424c4176SJason M. Bills class CrashdumpEntry : public Node 17571da66f75SEd Tanous { 17581da66f75SEd Tanous public: 1759424c4176SJason M. Bills CrashdumpEntry(CrowApp &app) : 1760d53dd41fSJason M. Bills Node(app, 1761424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/", 17621da66f75SEd Tanous std::string()) 17631da66f75SEd Tanous { 17641da66f75SEd Tanous entityPrivileges = { 1765e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1766e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1767e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1768e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1769e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1770e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 17711da66f75SEd Tanous } 17721da66f75SEd Tanous 17731da66f75SEd Tanous private: 17741da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 17751da66f75SEd Tanous const std::vector<std::string> ¶ms) override 17761da66f75SEd Tanous { 1777e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 17781da66f75SEd Tanous if (params.size() != 1) 17791da66f75SEd Tanous { 1780f12894f8SJason M. Bills messages::internalError(asyncResp->res); 17811da66f75SEd Tanous return; 17821da66f75SEd Tanous } 1783e855dd28SJason M. Bills const std::string &logID = params[0]; 1784e855dd28SJason M. Bills logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue); 1785e855dd28SJason M. Bills } 1786e855dd28SJason M. Bills }; 1787e855dd28SJason M. Bills 1788e855dd28SJason M. Bills class CrashdumpFile : public Node 1789e855dd28SJason M. Bills { 1790e855dd28SJason M. Bills public: 1791e855dd28SJason M. Bills CrashdumpFile(CrowApp &app) : 1792e855dd28SJason M. Bills Node(app, 1793e855dd28SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/" 1794e855dd28SJason M. Bills "<str>/", 1795e855dd28SJason M. Bills std::string(), std::string()) 1796e855dd28SJason M. Bills { 1797e855dd28SJason M. Bills entityPrivileges = { 1798e855dd28SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1799e855dd28SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1800e855dd28SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1801e855dd28SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1802e855dd28SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1803e855dd28SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1804e855dd28SJason M. Bills } 1805e855dd28SJason M. Bills 1806e855dd28SJason M. Bills private: 1807e855dd28SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1808e855dd28SJason M. Bills const std::vector<std::string> ¶ms) override 1809e855dd28SJason M. Bills { 1810e855dd28SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1811e855dd28SJason M. Bills if (params.size() != 2) 1812e855dd28SJason M. Bills { 1813e855dd28SJason M. Bills messages::internalError(asyncResp->res); 1814e855dd28SJason M. Bills return; 1815e855dd28SJason M. Bills } 1816e855dd28SJason M. Bills const std::string &logID = params[0]; 1817e855dd28SJason M. Bills const std::string &fileName = params[1]; 1818e855dd28SJason M. Bills 1819e855dd28SJason M. Bills auto getStoredLogCallback = [asyncResp, logID, fileName]( 1820abf2add6SEd Tanous const boost::system::error_code ec, 1821abf2add6SEd Tanous const std::variant<std::string> &resp) { 18221da66f75SEd Tanous if (ec) 18231da66f75SEd Tanous { 1824abf2add6SEd Tanous BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message(); 1825f12894f8SJason M. Bills messages::internalError(asyncResp->res); 18261da66f75SEd Tanous return; 18271da66f75SEd Tanous } 1828abf2add6SEd Tanous const std::string *log = std::get_if<std::string>(&resp); 18291da66f75SEd Tanous if (log == nullptr) 18301da66f75SEd Tanous { 1831f12894f8SJason M. Bills messages::internalError(asyncResp->res); 18321da66f75SEd Tanous return; 18331da66f75SEd Tanous } 1834e855dd28SJason M. Bills 1835e855dd28SJason M. Bills // Verify the file name parameter is correct 1836e855dd28SJason M. Bills if (fileName != getLogFileName(getLogCreatedTime(*log))) 18371da66f75SEd Tanous { 1838e855dd28SJason M. Bills messages::resourceMissingAtURI(asyncResp->res, fileName); 18391da66f75SEd Tanous return; 18401da66f75SEd Tanous } 1841e855dd28SJason M. Bills 1842e855dd28SJason M. Bills // Configure this to be a file download when accessed from a browser 1843e855dd28SJason M. Bills asyncResp->res.addHeader("Content-Disposition", "attachment"); 1844e855dd28SJason M. Bills asyncResp->res.body() = *log; 18451da66f75SEd Tanous }; 18461da66f75SEd Tanous crow::connections::systemBus->async_method_call( 18475b61b5e8SJason M. Bills std::move(getStoredLogCallback), crashdumpObject, 18485b61b5e8SJason M. Bills crashdumpPath + std::string("/") + logID, 18495b61b5e8SJason M. Bills "org.freedesktop.DBus.Properties", "Get", crashdumpInterface, 1850424c4176SJason M. Bills "Log"); 18511da66f75SEd Tanous } 18521da66f75SEd Tanous }; 18531da66f75SEd Tanous 1854424c4176SJason M. Bills class OnDemandCrashdump : public Node 18551da66f75SEd Tanous { 18561da66f75SEd Tanous public: 1857424c4176SJason M. Bills OnDemandCrashdump(CrowApp &app) : 1858424c4176SJason M. Bills Node(app, 1859424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/" 1860424c4176SJason M. Bills "Crashdump.OnDemand/") 18611da66f75SEd Tanous { 18621da66f75SEd Tanous entityPrivileges = { 1863e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1864e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1865e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1866e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1867e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1868e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 18691da66f75SEd Tanous } 18701da66f75SEd Tanous 18711da66f75SEd Tanous private: 18721da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 18731da66f75SEd Tanous const std::vector<std::string> ¶ms) override 18741da66f75SEd Tanous { 1875e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 18761da66f75SEd Tanous 187748e4639eSJason M. Bills auto generateonDemandLogCallback = 1878e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 18791da66f75SEd Tanous const std::string &resp) { 18801da66f75SEd Tanous if (ec) 18811da66f75SEd Tanous { 18821da66f75SEd Tanous if (ec.value() == 18831da66f75SEd Tanous boost::system::errc::operation_not_supported) 18841da66f75SEd Tanous { 1885f12894f8SJason M. Bills messages::resourceInStandby(asyncResp->res); 18861da66f75SEd Tanous } 18874363d3b2SJason M. Bills else if (ec.value() == 18884363d3b2SJason M. Bills boost::system::errc::device_or_resource_busy) 18894363d3b2SJason M. Bills { 18904363d3b2SJason M. Bills messages::serviceTemporarilyUnavailable(asyncResp->res, 18914363d3b2SJason M. Bills "60"); 18924363d3b2SJason M. Bills } 18931da66f75SEd Tanous else 18941da66f75SEd Tanous { 1895f12894f8SJason M. Bills messages::internalError(asyncResp->res); 18961da66f75SEd Tanous } 18971da66f75SEd Tanous return; 18981da66f75SEd Tanous } 18993e0414fdSJason M. Bills asyncResp->res.result(boost::beast::http::status::no_content); 19001da66f75SEd Tanous }; 19011da66f75SEd Tanous crow::connections::systemBus->async_method_call( 19025b61b5e8SJason M. Bills std::move(generateonDemandLogCallback), crashdumpObject, 19035b61b5e8SJason M. Bills crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog"); 19041da66f75SEd Tanous } 19051da66f75SEd Tanous }; 19061da66f75SEd Tanous 1907e1f26343SJason M. Bills class SendRawPECI : public Node 19081da66f75SEd Tanous { 19091da66f75SEd Tanous public: 1910e1f26343SJason M. Bills SendRawPECI(CrowApp &app) : 1911424c4176SJason M. Bills Node(app, 1912424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/" 1913424c4176SJason M. Bills "Crashdump.SendRawPeci/") 19141da66f75SEd Tanous { 19151da66f75SEd Tanous entityPrivileges = { 19161da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 19171da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 19181da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 19191da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 19201da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 19211da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 19221da66f75SEd Tanous } 19231da66f75SEd Tanous 19241da66f75SEd Tanous private: 19251da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 19261da66f75SEd Tanous const std::vector<std::string> ¶ms) override 19271da66f75SEd Tanous { 1928e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1929*8724c297SKarthick Sundarrajan std::vector<std::vector<uint8_t>> peciCommands; 1930*8724c297SKarthick Sundarrajan 1931*8724c297SKarthick Sundarrajan nlohmann::json reqJson = 1932*8724c297SKarthick Sundarrajan nlohmann::json::parse(req.body, nullptr, false); 1933*8724c297SKarthick Sundarrajan if (reqJson.find("PECICommands") != reqJson.end()) 1934*8724c297SKarthick Sundarrajan { 1935*8724c297SKarthick Sundarrajan if (!json_util::readJson(req, res, "PECICommands", peciCommands)) 1936*8724c297SKarthick Sundarrajan { 1937*8724c297SKarthick Sundarrajan return; 1938*8724c297SKarthick Sundarrajan } 1939*8724c297SKarthick Sundarrajan uint32_t idx = 0; 1940*8724c297SKarthick Sundarrajan for (auto const &cmd : peciCommands) 1941*8724c297SKarthick Sundarrajan { 1942*8724c297SKarthick Sundarrajan if (cmd.size() < 3) 1943*8724c297SKarthick Sundarrajan { 1944*8724c297SKarthick Sundarrajan std::string s("["); 1945*8724c297SKarthick Sundarrajan for (auto const &val : cmd) 1946*8724c297SKarthick Sundarrajan { 1947*8724c297SKarthick Sundarrajan if (val != *cmd.begin()) 1948*8724c297SKarthick Sundarrajan { 1949*8724c297SKarthick Sundarrajan s += ","; 1950*8724c297SKarthick Sundarrajan } 1951*8724c297SKarthick Sundarrajan s += std::to_string(val); 1952*8724c297SKarthick Sundarrajan } 1953*8724c297SKarthick Sundarrajan s += "]"; 1954*8724c297SKarthick Sundarrajan messages::actionParameterValueFormatError( 1955*8724c297SKarthick Sundarrajan res, s, "PECICommands[" + std::to_string(idx) + "]", 1956*8724c297SKarthick Sundarrajan "SendRawPeci"); 1957*8724c297SKarthick Sundarrajan return; 1958*8724c297SKarthick Sundarrajan } 1959*8724c297SKarthick Sundarrajan idx++; 1960*8724c297SKarthick Sundarrajan } 1961*8724c297SKarthick Sundarrajan } 1962*8724c297SKarthick Sundarrajan else 1963*8724c297SKarthick Sundarrajan { 1964*8724c297SKarthick Sundarrajan /* This interface is deprecated */ 1965b1556427SEd Tanous uint8_t clientAddress = 0; 1966b1556427SEd Tanous uint8_t readLength = 0; 19671da66f75SEd Tanous std::vector<uint8_t> peciCommand; 1968b1556427SEd Tanous if (!json_util::readJson(req, res, "ClientAddress", clientAddress, 1969b1556427SEd Tanous "ReadLength", readLength, "PECICommand", 1970b1556427SEd Tanous peciCommand)) 19711da66f75SEd Tanous { 19721da66f75SEd Tanous return; 19731da66f75SEd Tanous } 1974*8724c297SKarthick Sundarrajan peciCommands.push_back({clientAddress, 0, readLength}); 1975*8724c297SKarthick Sundarrajan peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(), 1976*8724c297SKarthick Sundarrajan peciCommand.end()); 1977*8724c297SKarthick Sundarrajan } 19781da66f75SEd Tanous // Callback to return the Raw PECI response 1979e1f26343SJason M. Bills auto sendRawPECICallback = 1980e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 1981*8724c297SKarthick Sundarrajan const std::vector<std::vector<uint8_t>> &resp) { 19821da66f75SEd Tanous if (ec) 19831da66f75SEd Tanous { 1984*8724c297SKarthick Sundarrajan BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: " 19851da66f75SEd Tanous << ec.message(); 1986f12894f8SJason M. Bills messages::internalError(asyncResp->res); 19871da66f75SEd Tanous return; 19881da66f75SEd Tanous } 1989e1f26343SJason M. Bills asyncResp->res.jsonValue = {{"Name", "PECI Command Response"}, 19901da66f75SEd Tanous {"PECIResponse", resp}}; 19911da66f75SEd Tanous }; 19921da66f75SEd Tanous // Call the SendRawPECI command with the provided data 19931da66f75SEd Tanous crow::connections::systemBus->async_method_call( 19945b61b5e8SJason M. Bills std::move(sendRawPECICallback), crashdumpObject, crashdumpPath, 1995*8724c297SKarthick Sundarrajan crashdumpRawPECIInterface, "SendRawPeci", peciCommands); 19961da66f75SEd Tanous } 19971da66f75SEd Tanous }; 19981da66f75SEd Tanous 1999cb92c03bSAndrew Geissler /** 2000cb92c03bSAndrew Geissler * DBusLogServiceActionsClear class supports POST method for ClearLog action. 2001cb92c03bSAndrew Geissler */ 2002cb92c03bSAndrew Geissler class DBusLogServiceActionsClear : public Node 2003cb92c03bSAndrew Geissler { 2004cb92c03bSAndrew Geissler public: 2005cb92c03bSAndrew Geissler DBusLogServiceActionsClear(CrowApp &app) : 2006cb92c03bSAndrew Geissler Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/" 20071f56a3a6STim Lee "LogService.ClearLog") 2008cb92c03bSAndrew Geissler { 2009cb92c03bSAndrew Geissler entityPrivileges = { 2010cb92c03bSAndrew Geissler {boost::beast::http::verb::get, {{"Login"}}}, 2011cb92c03bSAndrew Geissler {boost::beast::http::verb::head, {{"Login"}}}, 2012cb92c03bSAndrew Geissler {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 2013cb92c03bSAndrew Geissler {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 2014cb92c03bSAndrew Geissler {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 2015cb92c03bSAndrew Geissler {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 2016cb92c03bSAndrew Geissler } 2017cb92c03bSAndrew Geissler 2018cb92c03bSAndrew Geissler private: 2019cb92c03bSAndrew Geissler /** 2020cb92c03bSAndrew Geissler * Function handles POST method request. 2021cb92c03bSAndrew Geissler * The Clear Log actions does not require any parameter.The action deletes 2022cb92c03bSAndrew Geissler * all entries found in the Entries collection for this Log Service. 2023cb92c03bSAndrew Geissler */ 2024cb92c03bSAndrew Geissler void doPost(crow::Response &res, const crow::Request &req, 2025cb92c03bSAndrew Geissler const std::vector<std::string> ¶ms) override 2026cb92c03bSAndrew Geissler { 2027cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "Do delete all entries."; 2028cb92c03bSAndrew Geissler 2029cb92c03bSAndrew Geissler auto asyncResp = std::make_shared<AsyncResp>(res); 2030cb92c03bSAndrew Geissler // Process response from Logging service. 2031cb92c03bSAndrew Geissler auto resp_handler = [asyncResp](const boost::system::error_code ec) { 2032cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done"; 2033cb92c03bSAndrew Geissler if (ec) 2034cb92c03bSAndrew Geissler { 2035cb92c03bSAndrew Geissler // TODO Handle for specific error code 2036cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec; 2037cb92c03bSAndrew Geissler asyncResp->res.result( 2038cb92c03bSAndrew Geissler boost::beast::http::status::internal_server_error); 2039cb92c03bSAndrew Geissler return; 2040cb92c03bSAndrew Geissler } 2041cb92c03bSAndrew Geissler 2042cb92c03bSAndrew Geissler asyncResp->res.result(boost::beast::http::status::no_content); 2043cb92c03bSAndrew Geissler }; 2044cb92c03bSAndrew Geissler 2045cb92c03bSAndrew Geissler // Make call to Logging service to request Clear Log 2046cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 2047cb92c03bSAndrew Geissler resp_handler, "xyz.openbmc_project.Logging", 2048cb92c03bSAndrew Geissler "/xyz/openbmc_project/logging", 2049cb92c03bSAndrew Geissler "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); 2050cb92c03bSAndrew Geissler } 2051cb92c03bSAndrew Geissler }; 20521da66f75SEd Tanous } // namespace redfish 2053