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" 19*4851d45dSJason M. Bills #include "registries.hpp" 20*4851d45dSJason M. Bills #include "registries/base_message_registry.hpp" 21*4851d45dSJason M. Bills #include "registries/openbmc_message_registry.hpp" 221da66f75SEd Tanous 23e1f26343SJason M. Bills #include <systemd/sd-journal.h> 24e1f26343SJason M. Bills 25*4851d45dSJason M. Bills #include <boost/algorithm/string/split.hpp> 26*4851d45dSJason M. Bills #include <boost/beast/core/span.hpp> 271da66f75SEd Tanous #include <boost/container/flat_map.hpp> 28cb92c03bSAndrew Geissler #include <error_messages.hpp> 294418c7f0SJames Feist #include <filesystem> 30abf2add6SEd Tanous #include <variant> 311da66f75SEd Tanous 321da66f75SEd Tanous namespace redfish 331da66f75SEd Tanous { 341da66f75SEd Tanous 35424c4176SJason M. Bills constexpr char const *CrashdumpObject = "com.intel.crashdump"; 36424c4176SJason M. Bills constexpr char const *CrashdumpPath = "/com/intel/crashdump"; 37424c4176SJason M. Bills constexpr char const *CrashdumpOnDemandPath = "/com/intel/crashdump/OnDemand"; 38424c4176SJason M. Bills constexpr char const *CrashdumpInterface = "com.intel.crashdump"; 39424c4176SJason M. Bills constexpr char const *CrashdumpOnDemandInterface = 40424c4176SJason M. Bills "com.intel.crashdump.OnDemand"; 41424c4176SJason M. Bills constexpr char const *CrashdumpRawPECIInterface = 42424c4176SJason M. Bills "com.intel.crashdump.SendRawPeci"; 431da66f75SEd Tanous 44*4851d45dSJason M. Bills namespace message_registries 45*4851d45dSJason M. Bills { 46*4851d45dSJason M. Bills static const Message *getMessageFromRegistry( 47*4851d45dSJason M. Bills const std::string &messageKey, 48*4851d45dSJason M. Bills const boost::beast::span<const MessageEntry> registry) 49*4851d45dSJason M. Bills { 50*4851d45dSJason M. Bills boost::beast::span<const MessageEntry>::const_iterator messageIt = 51*4851d45dSJason M. Bills std::find_if(registry.cbegin(), registry.cend(), 52*4851d45dSJason M. Bills [&messageKey](const MessageEntry &messageEntry) { 53*4851d45dSJason M. Bills return !std::strcmp(messageEntry.first, 54*4851d45dSJason M. Bills messageKey.c_str()); 55*4851d45dSJason M. Bills }); 56*4851d45dSJason M. Bills if (messageIt != registry.cend()) 57*4851d45dSJason M. Bills { 58*4851d45dSJason M. Bills return &messageIt->second; 59*4851d45dSJason M. Bills } 60*4851d45dSJason M. Bills 61*4851d45dSJason M. Bills return nullptr; 62*4851d45dSJason M. Bills } 63*4851d45dSJason M. Bills 64*4851d45dSJason M. Bills static const Message *getMessage(const std::string_view &messageID) 65*4851d45dSJason M. Bills { 66*4851d45dSJason M. Bills // Redfish MessageIds are in the form 67*4851d45dSJason M. Bills // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find 68*4851d45dSJason M. Bills // the right Message 69*4851d45dSJason M. Bills std::vector<std::string> fields; 70*4851d45dSJason M. Bills fields.reserve(4); 71*4851d45dSJason M. Bills boost::split(fields, messageID, boost::is_any_of(".")); 72*4851d45dSJason M. Bills std::string ®istryName = fields[0]; 73*4851d45dSJason M. Bills std::string &messageKey = fields[3]; 74*4851d45dSJason M. Bills 75*4851d45dSJason M. Bills // Find the right registry and check it for the MessageKey 76*4851d45dSJason M. Bills if (std::string(base::header.registryPrefix) == registryName) 77*4851d45dSJason M. Bills { 78*4851d45dSJason M. Bills return getMessageFromRegistry( 79*4851d45dSJason M. Bills messageKey, boost::beast::span<const MessageEntry>(base::registry)); 80*4851d45dSJason M. Bills } 81*4851d45dSJason M. Bills if (std::string(openbmc::header.registryPrefix) == registryName) 82*4851d45dSJason M. Bills { 83*4851d45dSJason M. Bills return getMessageFromRegistry( 84*4851d45dSJason M. Bills messageKey, 85*4851d45dSJason M. Bills boost::beast::span<const MessageEntry>(openbmc::registry)); 86*4851d45dSJason M. Bills } 87*4851d45dSJason M. Bills return nullptr; 88*4851d45dSJason M. Bills } 89*4851d45dSJason M. Bills } // namespace message_registries 90*4851d45dSJason M. Bills 91f6150403SJames Feist namespace fs = std::filesystem; 921da66f75SEd Tanous 93cb92c03bSAndrew Geissler using GetManagedPropertyType = boost::container::flat_map< 94cb92c03bSAndrew Geissler std::string, 95cb92c03bSAndrew Geissler sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t, 96cb92c03bSAndrew Geissler int32_t, uint32_t, int64_t, uint64_t, double>>; 97cb92c03bSAndrew Geissler 98cb92c03bSAndrew Geissler using GetManagedObjectsType = boost::container::flat_map< 99cb92c03bSAndrew Geissler sdbusplus::message::object_path, 100cb92c03bSAndrew Geissler boost::container::flat_map<std::string, GetManagedPropertyType>>; 101cb92c03bSAndrew Geissler 102cb92c03bSAndrew Geissler inline std::string translateSeverityDbusToRedfish(const std::string &s) 103cb92c03bSAndrew Geissler { 104cb92c03bSAndrew Geissler if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert") 105cb92c03bSAndrew Geissler { 106cb92c03bSAndrew Geissler return "Critical"; 107cb92c03bSAndrew Geissler } 108cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") 109cb92c03bSAndrew Geissler { 110cb92c03bSAndrew Geissler return "Critical"; 111cb92c03bSAndrew Geissler } 112cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug") 113cb92c03bSAndrew Geissler { 114cb92c03bSAndrew Geissler return "OK"; 115cb92c03bSAndrew Geissler } 116cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") 117cb92c03bSAndrew Geissler { 118cb92c03bSAndrew Geissler return "Critical"; 119cb92c03bSAndrew Geissler } 120cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error") 121cb92c03bSAndrew Geissler { 122cb92c03bSAndrew Geissler return "Critical"; 123cb92c03bSAndrew Geissler } 124cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") 125cb92c03bSAndrew Geissler { 126cb92c03bSAndrew Geissler return "OK"; 127cb92c03bSAndrew Geissler } 128cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice") 129cb92c03bSAndrew Geissler { 130cb92c03bSAndrew Geissler return "OK"; 131cb92c03bSAndrew Geissler } 132cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning") 133cb92c03bSAndrew Geissler { 134cb92c03bSAndrew Geissler return "Warning"; 135cb92c03bSAndrew Geissler } 136cb92c03bSAndrew Geissler return ""; 137cb92c03bSAndrew Geissler } 138cb92c03bSAndrew Geissler 13916428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal, 14039e77504SEd Tanous const std::string_view &field, 14139e77504SEd Tanous std::string_view &contents) 14216428a1aSJason M. Bills { 14316428a1aSJason M. Bills const char *data = nullptr; 14416428a1aSJason M. Bills size_t length = 0; 14516428a1aSJason M. Bills int ret = 0; 14616428a1aSJason M. Bills // Get the metadata from the requested field of the journal entry 147b01bf299SEd Tanous ret = sd_journal_get_data(journal, field.data(), (const void **)&data, 148b01bf299SEd Tanous &length); 14916428a1aSJason M. Bills if (ret < 0) 15016428a1aSJason M. Bills { 15116428a1aSJason M. Bills return ret; 15216428a1aSJason M. Bills } 15339e77504SEd Tanous contents = std::string_view(data, length); 15416428a1aSJason M. Bills // Only use the content after the "=" character. 15516428a1aSJason M. Bills contents.remove_prefix(std::min(contents.find("=") + 1, contents.size())); 15616428a1aSJason M. Bills return ret; 15716428a1aSJason M. Bills } 15816428a1aSJason M. Bills 15916428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal, 16039e77504SEd Tanous const std::string_view &field, const int &base, 16116428a1aSJason M. Bills int &contents) 16216428a1aSJason M. Bills { 16316428a1aSJason M. Bills int ret = 0; 16439e77504SEd Tanous std::string_view metadata; 16516428a1aSJason M. Bills // Get the metadata from the requested field of the journal entry 16616428a1aSJason M. Bills ret = getJournalMetadata(journal, field, metadata); 16716428a1aSJason M. Bills if (ret < 0) 16816428a1aSJason M. Bills { 16916428a1aSJason M. Bills return ret; 17016428a1aSJason M. Bills } 171b01bf299SEd Tanous contents = strtol(metadata.data(), nullptr, base); 17216428a1aSJason M. Bills return ret; 17316428a1aSJason M. Bills } 17416428a1aSJason M. Bills 17516428a1aSJason M. Bills static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp) 17616428a1aSJason M. Bills { 17716428a1aSJason M. Bills int ret = 0; 17816428a1aSJason M. Bills uint64_t timestamp = 0; 17916428a1aSJason M. Bills ret = sd_journal_get_realtime_usec(journal, ×tamp); 18016428a1aSJason M. Bills if (ret < 0) 18116428a1aSJason M. Bills { 18216428a1aSJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 18316428a1aSJason M. Bills << strerror(-ret); 18416428a1aSJason M. Bills return false; 18516428a1aSJason M. Bills } 18616428a1aSJason M. Bills time_t t = 18716428a1aSJason M. Bills static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s 18816428a1aSJason M. Bills struct tm *loctime = localtime(&t); 18916428a1aSJason M. Bills char entryTime[64] = {}; 19016428a1aSJason M. Bills if (NULL != loctime) 19116428a1aSJason M. Bills { 19216428a1aSJason M. Bills strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime); 19316428a1aSJason M. Bills } 19416428a1aSJason M. Bills // Insert the ':' into the timezone 19539e77504SEd Tanous std::string_view t1(entryTime); 19639e77504SEd Tanous std::string_view t2(entryTime); 19716428a1aSJason M. Bills if (t1.size() > 2 && t2.size() > 2) 19816428a1aSJason M. Bills { 19916428a1aSJason M. Bills t1.remove_suffix(2); 20016428a1aSJason M. Bills t2.remove_prefix(t2.size() - 2); 20116428a1aSJason M. Bills } 20239e77504SEd Tanous entryTimestamp = std::string(t1) + ":" + std::string(t2); 20316428a1aSJason M. Bills return true; 20416428a1aSJason M. Bills } 20516428a1aSJason M. Bills 20616428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req, 20716428a1aSJason M. Bills long &skip) 20816428a1aSJason M. Bills { 20916428a1aSJason M. Bills char *skipParam = req.urlParams.get("$skip"); 21016428a1aSJason M. Bills if (skipParam != nullptr) 21116428a1aSJason M. Bills { 21216428a1aSJason M. Bills char *ptr = nullptr; 21316428a1aSJason M. Bills skip = std::strtol(skipParam, &ptr, 10); 21416428a1aSJason M. Bills if (*skipParam == '\0' || *ptr != '\0') 21516428a1aSJason M. Bills { 21616428a1aSJason M. Bills 21716428a1aSJason M. Bills messages::queryParameterValueTypeError(res, std::string(skipParam), 21816428a1aSJason M. Bills "$skip"); 21916428a1aSJason M. Bills return false; 22016428a1aSJason M. Bills } 22116428a1aSJason M. Bills if (skip < 0) 22216428a1aSJason M. Bills { 22316428a1aSJason M. Bills 22416428a1aSJason M. Bills messages::queryParameterOutOfRange(res, std::to_string(skip), 22516428a1aSJason M. Bills "$skip", "greater than 0"); 22616428a1aSJason M. Bills return false; 22716428a1aSJason M. Bills } 22816428a1aSJason M. Bills } 22916428a1aSJason M. Bills return true; 23016428a1aSJason M. Bills } 23116428a1aSJason M. Bills 23216428a1aSJason M. Bills static constexpr const long maxEntriesPerPage = 1000; 23316428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req, 23416428a1aSJason M. Bills long &top) 23516428a1aSJason M. Bills { 23616428a1aSJason M. Bills char *topParam = req.urlParams.get("$top"); 23716428a1aSJason M. Bills if (topParam != nullptr) 23816428a1aSJason M. Bills { 23916428a1aSJason M. Bills char *ptr = nullptr; 24016428a1aSJason M. Bills top = std::strtol(topParam, &ptr, 10); 24116428a1aSJason M. Bills if (*topParam == '\0' || *ptr != '\0') 24216428a1aSJason M. Bills { 24316428a1aSJason M. Bills messages::queryParameterValueTypeError(res, std::string(topParam), 24416428a1aSJason M. Bills "$top"); 24516428a1aSJason M. Bills return false; 24616428a1aSJason M. Bills } 24716428a1aSJason M. Bills if (top < 1 || top > maxEntriesPerPage) 24816428a1aSJason M. Bills { 24916428a1aSJason M. Bills 25016428a1aSJason M. Bills messages::queryParameterOutOfRange( 25116428a1aSJason M. Bills res, std::to_string(top), "$top", 25216428a1aSJason M. Bills "1-" + std::to_string(maxEntriesPerPage)); 25316428a1aSJason M. Bills return false; 25416428a1aSJason M. Bills } 25516428a1aSJason M. Bills } 25616428a1aSJason M. Bills return true; 25716428a1aSJason M. Bills } 25816428a1aSJason M. Bills 25916428a1aSJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID) 26016428a1aSJason M. Bills { 26116428a1aSJason M. Bills int ret = 0; 26216428a1aSJason M. Bills static uint64_t prevTs = 0; 26316428a1aSJason M. Bills static int index = 0; 26416428a1aSJason M. Bills // Get the entry timestamp 26516428a1aSJason M. Bills uint64_t curTs = 0; 26616428a1aSJason M. Bills ret = sd_journal_get_realtime_usec(journal, &curTs); 26716428a1aSJason M. Bills if (ret < 0) 26816428a1aSJason M. Bills { 26916428a1aSJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 27016428a1aSJason M. Bills << strerror(-ret); 27116428a1aSJason M. Bills return false; 27216428a1aSJason M. Bills } 27316428a1aSJason M. Bills // If the timestamp isn't unique, increment the index 27416428a1aSJason M. Bills if (curTs == prevTs) 27516428a1aSJason M. Bills { 27616428a1aSJason M. Bills index++; 27716428a1aSJason M. Bills } 27816428a1aSJason M. Bills else 27916428a1aSJason M. Bills { 28016428a1aSJason M. Bills // Otherwise, reset it 28116428a1aSJason M. Bills index = 0; 28216428a1aSJason M. Bills } 28316428a1aSJason M. Bills // Save the timestamp 28416428a1aSJason M. Bills prevTs = curTs; 28516428a1aSJason M. Bills 28616428a1aSJason M. Bills entryID = std::to_string(curTs); 28716428a1aSJason M. Bills if (index > 0) 28816428a1aSJason M. Bills { 28916428a1aSJason M. Bills entryID += "_" + std::to_string(index); 29016428a1aSJason M. Bills } 29116428a1aSJason M. Bills return true; 29216428a1aSJason M. Bills } 29316428a1aSJason M. Bills 29416428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID, 29516428a1aSJason M. Bills uint64_t ×tamp, uint16_t &index) 29616428a1aSJason M. Bills { 29716428a1aSJason M. Bills if (entryID.empty()) 29816428a1aSJason M. Bills { 29916428a1aSJason M. Bills return false; 30016428a1aSJason M. Bills } 30116428a1aSJason M. Bills // Convert the unique ID back to a timestamp to find the entry 30239e77504SEd Tanous std::string_view tsStr(entryID); 30316428a1aSJason M. Bills 30416428a1aSJason M. Bills auto underscorePos = tsStr.find("_"); 30516428a1aSJason M. Bills if (underscorePos != tsStr.npos) 30616428a1aSJason M. Bills { 30716428a1aSJason M. Bills // Timestamp has an index 30816428a1aSJason M. Bills tsStr.remove_suffix(tsStr.size() - underscorePos); 30939e77504SEd Tanous std::string_view indexStr(entryID); 31016428a1aSJason M. Bills indexStr.remove_prefix(underscorePos + 1); 31116428a1aSJason M. Bills std::size_t pos; 31216428a1aSJason M. Bills try 31316428a1aSJason M. Bills { 31439e77504SEd Tanous index = std::stoul(std::string(indexStr), &pos); 31516428a1aSJason M. Bills } 316b01bf299SEd Tanous catch (std::invalid_argument) 31716428a1aSJason M. Bills { 31816428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 31916428a1aSJason M. Bills return false; 32016428a1aSJason M. Bills } 321b01bf299SEd Tanous catch (std::out_of_range) 32216428a1aSJason M. Bills { 32316428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 32416428a1aSJason M. Bills return false; 32516428a1aSJason M. Bills } 32616428a1aSJason M. Bills if (pos != indexStr.size()) 32716428a1aSJason M. Bills { 32816428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 32916428a1aSJason M. Bills return false; 33016428a1aSJason M. Bills } 33116428a1aSJason M. Bills } 33216428a1aSJason M. Bills // Timestamp has no index 33316428a1aSJason M. Bills std::size_t pos; 33416428a1aSJason M. Bills try 33516428a1aSJason M. Bills { 33639e77504SEd Tanous timestamp = std::stoull(std::string(tsStr), &pos); 33716428a1aSJason M. Bills } 338b01bf299SEd Tanous catch (std::invalid_argument) 33916428a1aSJason M. Bills { 34016428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 34116428a1aSJason M. Bills return false; 34216428a1aSJason M. Bills } 343b01bf299SEd Tanous catch (std::out_of_range) 34416428a1aSJason M. Bills { 34516428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 34616428a1aSJason M. Bills return false; 34716428a1aSJason M. Bills } 34816428a1aSJason M. Bills if (pos != tsStr.size()) 34916428a1aSJason M. Bills { 35016428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 35116428a1aSJason M. Bills return false; 35216428a1aSJason M. Bills } 35316428a1aSJason M. Bills return true; 35416428a1aSJason M. Bills } 35516428a1aSJason M. Bills 356c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node 3571da66f75SEd Tanous { 3581da66f75SEd Tanous public: 3591da66f75SEd Tanous template <typename CrowApp> 360c4bf6374SJason M. Bills SystemLogServiceCollection(CrowApp &app) : 361029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/") 362c4bf6374SJason M. Bills { 363c4bf6374SJason M. Bills entityPrivileges = { 364c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 365c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 366c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 367c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 368c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 369c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 370c4bf6374SJason M. Bills } 371c4bf6374SJason M. Bills 372c4bf6374SJason M. Bills private: 373c4bf6374SJason M. Bills /** 374c4bf6374SJason M. Bills * Functions triggers appropriate requests on DBus 375c4bf6374SJason M. Bills */ 376c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 377c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 378c4bf6374SJason M. Bills { 379c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 380c4bf6374SJason M. Bills // Collections don't include the static data added by SubRoute because 381c4bf6374SJason M. Bills // it has a duplicate entry for members 382c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 383c4bf6374SJason M. Bills "#LogServiceCollection.LogServiceCollection"; 384c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 385c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection"; 386c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 387029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices"; 388c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "System Log Services Collection"; 389c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = 390c4bf6374SJason M. Bills "Collection of LogServices for this Computer System"; 391c4bf6374SJason M. Bills nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; 392c4bf6374SJason M. Bills logServiceArray = nlohmann::json::array(); 393029573d4SEd Tanous logServiceArray.push_back( 394029573d4SEd Tanous {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}}); 395d53dd41fSJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG 396d53dd41fSJason M. Bills logServiceArray.push_back( 397cb92c03bSAndrew Geissler {{ "@odata.id", 398424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump" }}); 399d53dd41fSJason M. Bills #endif 400c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 401c4bf6374SJason M. Bills logServiceArray.size(); 402c4bf6374SJason M. Bills } 403c4bf6374SJason M. Bills }; 404c4bf6374SJason M. Bills 405c4bf6374SJason M. Bills class EventLogService : public Node 406c4bf6374SJason M. Bills { 407c4bf6374SJason M. Bills public: 408c4bf6374SJason M. Bills template <typename CrowApp> 409c4bf6374SJason M. Bills EventLogService(CrowApp &app) : 410029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/") 411c4bf6374SJason M. Bills { 412c4bf6374SJason M. Bills entityPrivileges = { 413c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 414c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 415c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 416c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 417c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 418c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 419c4bf6374SJason M. Bills } 420c4bf6374SJason M. Bills 421c4bf6374SJason M. Bills private: 422c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 423c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 424c4bf6374SJason M. Bills { 425c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 426c4bf6374SJason M. Bills 427c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 428029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog"; 429c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 430c4bf6374SJason M. Bills "#LogService.v1_1_0.LogService"; 431c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 432c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 433c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "Event Log Service"; 434c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = "System Event Log Service"; 435c4bf6374SJason M. Bills asyncResp->res.jsonValue["Id"] = "Event Log"; 436c4bf6374SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 437c4bf6374SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 438c4bf6374SJason M. Bills {"@odata.id", 439029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}}; 440c4bf6374SJason M. Bills } 441c4bf6374SJason M. Bills }; 442c4bf6374SJason M. Bills 443029573d4SEd Tanous static int fillEventLogEntryJson(const std::string &bmcLogEntryID, 44439e77504SEd Tanous const std::string_view &messageID, 445c4bf6374SJason M. Bills sd_journal *journal, 446c4bf6374SJason M. Bills nlohmann::json &bmcLogEntryJson) 447c4bf6374SJason M. Bills { 448*4851d45dSJason M. Bills // Get the Message from the MessageRegistry 449*4851d45dSJason M. Bills const message_registries::Message *message = 450*4851d45dSJason M. Bills message_registries::getMessage(messageID); 451c4bf6374SJason M. Bills 452*4851d45dSJason M. Bills std::string msg; 453*4851d45dSJason M. Bills std::string severity; 454*4851d45dSJason M. Bills if (message != nullptr) 455c4bf6374SJason M. Bills { 456*4851d45dSJason M. Bills msg = message->message; 457*4851d45dSJason M. Bills severity = message->severity; 458c4bf6374SJason M. Bills } 459c4bf6374SJason M. Bills 460c4bf6374SJason M. Bills // Get the MessageArgs from the journal entry by finding all of the 461c4bf6374SJason M. Bills // REDFISH_MESSAGE_ARG_x fields 462c4bf6374SJason M. Bills const void *data; 463c4bf6374SJason M. Bills size_t length; 464c4bf6374SJason M. Bills std::vector<std::string> messageArgs; 465c4bf6374SJason M. Bills SD_JOURNAL_FOREACH_DATA(journal, data, length) 466c4bf6374SJason M. Bills { 46739e77504SEd Tanous std::string_view field(static_cast<const char *>(data), length); 46839e77504SEd Tanous if (boost::starts_with(field, "REDFISH_MESSAGE_ARG_")) 469c4bf6374SJason M. Bills { 470c4bf6374SJason M. Bills // Get the Arg number from the field name 471c4bf6374SJason M. Bills field.remove_prefix(sizeof("REDFISH_MESSAGE_ARG_") - 1); 472c4bf6374SJason M. Bills if (field.empty()) 473c4bf6374SJason M. Bills { 474c4bf6374SJason M. Bills continue; 475c4bf6374SJason M. Bills } 476b01bf299SEd Tanous int argNum = std::strtoul(field.data(), nullptr, 10); 477b01bf299SEd Tanous if (argNum == 0) 478c4bf6374SJason M. Bills { 479c4bf6374SJason M. Bills continue; 480c4bf6374SJason M. Bills } 481c4bf6374SJason M. Bills // Get the Arg value after the "=" character. 482c4bf6374SJason M. Bills field.remove_prefix(std::min(field.find("=") + 1, field.size())); 483c4bf6374SJason M. Bills // Make sure we have enough space in messageArgs 484c4bf6374SJason M. Bills if (argNum > messageArgs.size()) 485c4bf6374SJason M. Bills { 486b01bf299SEd Tanous messageArgs.resize(argNum); 487c4bf6374SJason M. Bills } 48839e77504SEd Tanous messageArgs[argNum - 1] = std::string(field); 489c4bf6374SJason M. Bills } 490c4bf6374SJason M. Bills } 491c4bf6374SJason M. Bills 492*4851d45dSJason M. Bills // Fill the MessageArgs into the Message 493*4851d45dSJason M. Bills for (size_t i = 0; i < messageArgs.size(); i++) 494*4851d45dSJason M. Bills { 495*4851d45dSJason M. Bills std::string argStr = "%" + std::to_string(i + 1); 496*4851d45dSJason M. Bills size_t argPos = msg.find(argStr); 497*4851d45dSJason M. Bills if (argPos != std::string::npos) 498*4851d45dSJason M. Bills { 499*4851d45dSJason M. Bills msg.replace(argPos, argStr.length(), messageArgs[i]); 500*4851d45dSJason M. Bills } 501*4851d45dSJason M. Bills } 502*4851d45dSJason M. Bills 503c4bf6374SJason M. Bills // Get the Created time from the timestamp 504c4bf6374SJason M. Bills std::string entryTimeStr; 505c4bf6374SJason M. Bills if (!getEntryTimestamp(journal, entryTimeStr)) 506c4bf6374SJason M. Bills { 507c4bf6374SJason M. Bills return 1; 508c4bf6374SJason M. Bills } 509c4bf6374SJason M. Bills 510c4bf6374SJason M. Bills // Fill in the log entry with the gathered data 511c4bf6374SJason M. Bills bmcLogEntryJson = { 512cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 513c4bf6374SJason M. Bills {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 514029573d4SEd Tanous {"@odata.id", 515029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + 516029573d4SEd Tanous bmcLogEntryID}, 517c4bf6374SJason M. Bills {"Name", "System Event Log Entry"}, 518c4bf6374SJason M. Bills {"Id", bmcLogEntryID}, 519c4bf6374SJason M. Bills {"Message", msg}, 520c4bf6374SJason M. Bills {"MessageId", messageID}, 521c4bf6374SJason M. Bills {"MessageArgs", std::move(messageArgs)}, 522c4bf6374SJason M. Bills {"EntryType", "Event"}, 523*4851d45dSJason M. Bills {"Severity", severity}, 524c4bf6374SJason M. Bills {"Created", std::move(entryTimeStr)}}; 525c4bf6374SJason M. Bills return 0; 526c4bf6374SJason M. Bills } 527c4bf6374SJason M. Bills 528c4bf6374SJason M. Bills class EventLogEntryCollection : public Node 529c4bf6374SJason M. Bills { 530c4bf6374SJason M. Bills public: 531c4bf6374SJason M. Bills template <typename CrowApp> 532c4bf6374SJason M. Bills EventLogEntryCollection(CrowApp &app) : 533029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/") 534c4bf6374SJason M. Bills { 535c4bf6374SJason M. Bills entityPrivileges = { 536c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 537c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 538c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 539c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 540c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 541c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 542c4bf6374SJason M. Bills } 543c4bf6374SJason M. Bills 544c4bf6374SJason M. Bills private: 545c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 546c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 547c4bf6374SJason M. Bills { 548c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 549c4bf6374SJason M. Bills long skip = 0; 550c4bf6374SJason M. Bills long top = maxEntriesPerPage; // Show max entries by default 551c4bf6374SJason M. Bills if (!getSkipParam(asyncResp->res, req, skip)) 552c4bf6374SJason M. Bills { 553c4bf6374SJason M. Bills return; 554c4bf6374SJason M. Bills } 555c4bf6374SJason M. Bills if (!getTopParam(asyncResp->res, req, top)) 556c4bf6374SJason M. Bills { 557c4bf6374SJason M. Bills return; 558c4bf6374SJason M. Bills } 559c4bf6374SJason M. Bills // Collections don't include the static data added by SubRoute because 560c4bf6374SJason M. Bills // it has a duplicate entry for members 561c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 562c4bf6374SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 563c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 564c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection"; 565c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 566029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries"; 567c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 568c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = 569c4bf6374SJason M. Bills "Collection of System Event Log Entries"; 570cb92c03bSAndrew Geissler 571cb92c03bSAndrew Geissler #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES 572c4bf6374SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 573c4bf6374SJason M. Bills logEntryArray = nlohmann::json::array(); 574c4bf6374SJason M. Bills // Go through the journal and create a unique ID for each entry 575c4bf6374SJason M. Bills sd_journal *journalTmp = nullptr; 576c4bf6374SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 577c4bf6374SJason M. Bills if (ret < 0) 578c4bf6374SJason M. Bills { 579c4bf6374SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 580c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 581c4bf6374SJason M. Bills return; 582c4bf6374SJason M. Bills } 583c4bf6374SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 584c4bf6374SJason M. Bills journalTmp, sd_journal_close); 585c4bf6374SJason M. Bills journalTmp = nullptr; 586b01bf299SEd Tanous uint64_t entryCount = 0; 587c4bf6374SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 588c4bf6374SJason M. Bills { 589c4bf6374SJason M. Bills // Look for only journal entries that contain a REDFISH_MESSAGE_ID 590c4bf6374SJason M. Bills // field 59139e77504SEd Tanous std::string_view messageID; 592c4bf6374SJason M. Bills ret = getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", 593c4bf6374SJason M. Bills messageID); 594c4bf6374SJason M. Bills if (ret < 0) 595c4bf6374SJason M. Bills { 596c4bf6374SJason M. Bills continue; 597c4bf6374SJason M. Bills } 598c4bf6374SJason M. Bills 599c4bf6374SJason M. Bills entryCount++; 600c4bf6374SJason M. Bills // Handle paging using skip (number of entries to skip from the 601c4bf6374SJason M. Bills // start) and top (number of entries to display) 602c4bf6374SJason M. Bills if (entryCount <= skip || entryCount > skip + top) 603c4bf6374SJason M. Bills { 604c4bf6374SJason M. Bills continue; 605c4bf6374SJason M. Bills } 606c4bf6374SJason M. Bills 607c4bf6374SJason M. Bills std::string idStr; 608c4bf6374SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr)) 609c4bf6374SJason M. Bills { 610c4bf6374SJason M. Bills continue; 611c4bf6374SJason M. Bills } 612c4bf6374SJason M. Bills 613c4bf6374SJason M. Bills logEntryArray.push_back({}); 614c4bf6374SJason M. Bills nlohmann::json &bmcLogEntry = logEntryArray.back(); 615029573d4SEd Tanous if (fillEventLogEntryJson(idStr, messageID, journal.get(), 616c4bf6374SJason M. Bills bmcLogEntry) != 0) 617c4bf6374SJason M. Bills { 618c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 619c4bf6374SJason M. Bills return; 620c4bf6374SJason M. Bills } 621c4bf6374SJason M. Bills } 622c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 623c4bf6374SJason M. Bills if (skip + top < entryCount) 624c4bf6374SJason M. Bills { 625c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 626c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" + 627c4bf6374SJason M. Bills std::to_string(skip + top); 628c4bf6374SJason M. Bills } 629cb92c03bSAndrew Geissler #else 630cb92c03bSAndrew Geissler // DBus implementation of EventLog/Entries 631cb92c03bSAndrew Geissler // Make call to Logging Service to find all log entry objects 632cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 633cb92c03bSAndrew Geissler [asyncResp](const boost::system::error_code ec, 634cb92c03bSAndrew Geissler GetManagedObjectsType &resp) { 635cb92c03bSAndrew Geissler if (ec) 636cb92c03bSAndrew Geissler { 637cb92c03bSAndrew Geissler // TODO Handle for specific error code 638cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR 639cb92c03bSAndrew Geissler << "getLogEntriesIfaceData resp_handler got error " 640cb92c03bSAndrew Geissler << ec; 641cb92c03bSAndrew Geissler messages::internalError(asyncResp->res); 642cb92c03bSAndrew Geissler return; 643cb92c03bSAndrew Geissler } 644cb92c03bSAndrew Geissler nlohmann::json &entriesArray = 645cb92c03bSAndrew Geissler asyncResp->res.jsonValue["Members"]; 646cb92c03bSAndrew Geissler entriesArray = nlohmann::json::array(); 647cb92c03bSAndrew Geissler for (auto &objectPath : resp) 648cb92c03bSAndrew Geissler { 649cb92c03bSAndrew Geissler for (auto &interfaceMap : objectPath.second) 650cb92c03bSAndrew Geissler { 651cb92c03bSAndrew Geissler if (interfaceMap.first != 652cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging.Entry") 653cb92c03bSAndrew Geissler { 654cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "Bailing early on " 655cb92c03bSAndrew Geissler << interfaceMap.first; 656cb92c03bSAndrew Geissler continue; 657cb92c03bSAndrew Geissler } 658cb92c03bSAndrew Geissler entriesArray.push_back({}); 659cb92c03bSAndrew Geissler nlohmann::json &thisEntry = entriesArray.back(); 660cb92c03bSAndrew Geissler uint32_t *id; 661cb92c03bSAndrew Geissler std::time_t timestamp; 662cb92c03bSAndrew Geissler std::string *severity, *message; 663cb92c03bSAndrew Geissler bool *resolved; 664cb92c03bSAndrew Geissler for (auto &propertyMap : interfaceMap.second) 665cb92c03bSAndrew Geissler { 666cb92c03bSAndrew Geissler if (propertyMap.first == "Id") 667cb92c03bSAndrew Geissler { 668cb92c03bSAndrew Geissler id = sdbusplus::message::variant_ns::get_if< 669cb92c03bSAndrew Geissler uint32_t>(&propertyMap.second); 670cb92c03bSAndrew Geissler if (id == nullptr) 671cb92c03bSAndrew Geissler { 672cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 673cb92c03bSAndrew Geissler "Id"); 674cb92c03bSAndrew Geissler } 675cb92c03bSAndrew Geissler } 676cb92c03bSAndrew Geissler else if (propertyMap.first == "Timestamp") 677cb92c03bSAndrew Geissler { 678cb92c03bSAndrew Geissler const uint64_t *millisTimeStamp = 679cb92c03bSAndrew Geissler std::get_if<uint64_t>(&propertyMap.second); 680cb92c03bSAndrew Geissler if (millisTimeStamp == nullptr) 681cb92c03bSAndrew Geissler { 682cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 683cb92c03bSAndrew Geissler "Timestamp"); 684cb92c03bSAndrew Geissler } 685cb92c03bSAndrew Geissler // Retrieve Created property with format: 686cb92c03bSAndrew Geissler // yyyy-mm-ddThh:mm:ss 687cb92c03bSAndrew Geissler std::chrono::milliseconds chronoTimeStamp( 688cb92c03bSAndrew Geissler *millisTimeStamp); 689cb92c03bSAndrew Geissler timestamp = 690cb92c03bSAndrew Geissler std::chrono::duration_cast< 691cb92c03bSAndrew Geissler std::chrono::seconds>(chronoTimeStamp) 692cb92c03bSAndrew Geissler .count(); 693cb92c03bSAndrew Geissler } 694cb92c03bSAndrew Geissler else if (propertyMap.first == "Severity") 695cb92c03bSAndrew Geissler { 696cb92c03bSAndrew Geissler severity = std::get_if<std::string>( 697cb92c03bSAndrew Geissler &propertyMap.second); 698cb92c03bSAndrew Geissler if (severity == nullptr) 699cb92c03bSAndrew Geissler { 700cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 701cb92c03bSAndrew Geissler "Severity"); 702cb92c03bSAndrew Geissler } 703cb92c03bSAndrew Geissler } 704cb92c03bSAndrew Geissler else if (propertyMap.first == "Message") 705cb92c03bSAndrew Geissler { 706cb92c03bSAndrew Geissler message = std::get_if<std::string>( 707cb92c03bSAndrew Geissler &propertyMap.second); 708cb92c03bSAndrew Geissler if (message == nullptr) 709cb92c03bSAndrew Geissler { 710cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 711cb92c03bSAndrew Geissler "Message"); 712cb92c03bSAndrew Geissler } 713cb92c03bSAndrew Geissler } 714cb92c03bSAndrew Geissler } 715cb92c03bSAndrew Geissler thisEntry = { 716cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 717cb92c03bSAndrew Geissler {"@odata.context", "/redfish/v1/" 718cb92c03bSAndrew Geissler "$metadata#LogEntry.LogEntry"}, 719cb92c03bSAndrew Geissler {"@odata.id", 720cb92c03bSAndrew Geissler "/redfish/v1/Systems/system/LogServices/EventLog/" 721cb92c03bSAndrew Geissler "Entries/" + 722cb92c03bSAndrew Geissler std::to_string(*id)}, 723cb92c03bSAndrew Geissler {"Name", "System DBus Event Log Entry"}, 724cb92c03bSAndrew Geissler {"Id", std::to_string(*id)}, 725cb92c03bSAndrew Geissler {"Message", *message}, 726cb92c03bSAndrew Geissler {"EntryType", "Event"}, 727cb92c03bSAndrew Geissler {"Severity", 728cb92c03bSAndrew Geissler translateSeverityDbusToRedfish(*severity)}, 729cb92c03bSAndrew Geissler {"Created", crow::utility::getDateTime(timestamp)}}; 730cb92c03bSAndrew Geissler } 731cb92c03bSAndrew Geissler } 732cb92c03bSAndrew Geissler std::sort(entriesArray.begin(), entriesArray.end(), 733cb92c03bSAndrew Geissler [](const nlohmann::json &left, 734cb92c03bSAndrew Geissler const nlohmann::json &right) { 735cb92c03bSAndrew Geissler return (left["Id"] <= right["Id"]); 736cb92c03bSAndrew Geissler }); 737cb92c03bSAndrew Geissler asyncResp->res.jsonValue["Members@odata.count"] = 738cb92c03bSAndrew Geissler entriesArray.size(); 739cb92c03bSAndrew Geissler }, 740cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging", 741cb92c03bSAndrew Geissler "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 742cb92c03bSAndrew Geissler #endif 743c4bf6374SJason M. Bills } 744c4bf6374SJason M. Bills }; 745c4bf6374SJason M. Bills 746c4bf6374SJason M. Bills class EventLogEntry : public Node 747c4bf6374SJason M. Bills { 748c4bf6374SJason M. Bills public: 749c4bf6374SJason M. Bills EventLogEntry(CrowApp &app) : 750c4bf6374SJason M. Bills Node(app, 751029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/", 752029573d4SEd Tanous std::string()) 753c4bf6374SJason M. Bills { 754c4bf6374SJason M. Bills entityPrivileges = { 755c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 756c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 757c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 758c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 759c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 760c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 761c4bf6374SJason M. Bills } 762c4bf6374SJason M. Bills 763c4bf6374SJason M. Bills private: 764c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 765c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 766c4bf6374SJason M. Bills { 767c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 768029573d4SEd Tanous if (params.size() != 1) 769c4bf6374SJason M. Bills { 770c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 771c4bf6374SJason M. Bills return; 772c4bf6374SJason M. Bills } 773029573d4SEd Tanous const std::string &entryID = params[0]; 774cb92c03bSAndrew Geissler 775cb92c03bSAndrew Geissler #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES 776c4bf6374SJason M. Bills // Convert the unique ID back to a timestamp to find the entry 777c4bf6374SJason M. Bills uint64_t ts = 0; 778c4bf6374SJason M. Bills uint16_t index = 0; 779c4bf6374SJason M. Bills if (!getTimestampFromID(asyncResp->res, entryID, ts, index)) 780c4bf6374SJason M. Bills { 781c4bf6374SJason M. Bills return; 782c4bf6374SJason M. Bills } 783c4bf6374SJason M. Bills 784c4bf6374SJason M. Bills sd_journal *journalTmp = nullptr; 785c4bf6374SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 786c4bf6374SJason M. Bills if (ret < 0) 787c4bf6374SJason M. Bills { 788c4bf6374SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 789c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 790c4bf6374SJason M. Bills return; 791c4bf6374SJason M. Bills } 792c4bf6374SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 793c4bf6374SJason M. Bills journalTmp, sd_journal_close); 794c4bf6374SJason M. Bills journalTmp = nullptr; 795c4bf6374SJason M. Bills // Go to the timestamp in the log and move to the entry at the index 796c4bf6374SJason M. Bills ret = sd_journal_seek_realtime_usec(journal.get(), ts); 797c4bf6374SJason M. Bills for (int i = 0; i <= index; i++) 798c4bf6374SJason M. Bills { 799c4bf6374SJason M. Bills sd_journal_next(journal.get()); 800c4bf6374SJason M. Bills } 801c4bf6374SJason M. Bills // Confirm that the entry ID matches what was requested 802c4bf6374SJason M. Bills std::string idStr; 803c4bf6374SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID) 804c4bf6374SJason M. Bills { 805c4bf6374SJason M. Bills messages::resourceMissingAtURI(asyncResp->res, entryID); 806c4bf6374SJason M. Bills return; 807c4bf6374SJason M. Bills } 808c4bf6374SJason M. Bills 809cd50aa42SJason M. Bills // only use journal entries that contain a REDFISH_MESSAGE_ID field 81039e77504SEd Tanous std::string_view messageID; 811c4bf6374SJason M. Bills ret = 812c4bf6374SJason M. Bills getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", messageID); 813c4bf6374SJason M. Bills if (ret < 0) 814c4bf6374SJason M. Bills { 815029573d4SEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", "system"); 816c4bf6374SJason M. Bills return; 817c4bf6374SJason M. Bills } 818c4bf6374SJason M. Bills 819029573d4SEd Tanous if (fillEventLogEntryJson(entryID, messageID, journal.get(), 820c4bf6374SJason M. Bills asyncResp->res.jsonValue) != 0) 821c4bf6374SJason M. Bills { 822c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 823c4bf6374SJason M. Bills return; 824c4bf6374SJason M. Bills } 825cb92c03bSAndrew Geissler #else 826cb92c03bSAndrew Geissler // DBus implementation of EventLog/Entries 827cb92c03bSAndrew Geissler // Make call to Logging Service to find all log entry objects 828cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 829cb92c03bSAndrew Geissler [asyncResp, entryID](const boost::system::error_code ec, 830cb92c03bSAndrew Geissler GetManagedPropertyType &resp) { 831cb92c03bSAndrew Geissler if (ec) 832cb92c03bSAndrew Geissler { 833cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR 834cb92c03bSAndrew Geissler << "EventLogEntry (DBus) resp_handler got error " << ec; 835cb92c03bSAndrew Geissler messages::internalError(asyncResp->res); 836cb92c03bSAndrew Geissler return; 837cb92c03bSAndrew Geissler } 838cb92c03bSAndrew Geissler uint32_t *id; 839cb92c03bSAndrew Geissler std::time_t timestamp; 840cb92c03bSAndrew Geissler std::string *severity, *message; 841cb92c03bSAndrew Geissler bool *resolved; 842cb92c03bSAndrew Geissler for (auto &propertyMap : resp) 843cb92c03bSAndrew Geissler { 844cb92c03bSAndrew Geissler if (propertyMap.first == "Id") 845cb92c03bSAndrew Geissler { 846cb92c03bSAndrew Geissler id = std::get_if<uint32_t>(&propertyMap.second); 847cb92c03bSAndrew Geissler if (id == nullptr) 848cb92c03bSAndrew Geissler { 849cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, "Id"); 850cb92c03bSAndrew Geissler } 851cb92c03bSAndrew Geissler } 852cb92c03bSAndrew Geissler else if (propertyMap.first == "Timestamp") 853cb92c03bSAndrew Geissler { 854cb92c03bSAndrew Geissler const uint64_t *millisTimeStamp = 855cb92c03bSAndrew Geissler std::get_if<uint64_t>(&propertyMap.second); 856cb92c03bSAndrew Geissler if (millisTimeStamp == nullptr) 857cb92c03bSAndrew Geissler { 858cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 859cb92c03bSAndrew Geissler "Timestamp"); 860cb92c03bSAndrew Geissler } 861cb92c03bSAndrew Geissler // Retrieve Created property with format: 862cb92c03bSAndrew Geissler // yyyy-mm-ddThh:mm:ss 863cb92c03bSAndrew Geissler std::chrono::milliseconds chronoTimeStamp( 864cb92c03bSAndrew Geissler *millisTimeStamp); 865cb92c03bSAndrew Geissler timestamp = 866cb92c03bSAndrew Geissler std::chrono::duration_cast<std::chrono::seconds>( 867cb92c03bSAndrew Geissler chronoTimeStamp) 868cb92c03bSAndrew Geissler .count(); 869cb92c03bSAndrew Geissler } 870cb92c03bSAndrew Geissler else if (propertyMap.first == "Severity") 871cb92c03bSAndrew Geissler { 872cb92c03bSAndrew Geissler severity = 873cb92c03bSAndrew Geissler std::get_if<std::string>(&propertyMap.second); 874cb92c03bSAndrew Geissler if (severity == nullptr) 875cb92c03bSAndrew Geissler { 876cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 877cb92c03bSAndrew Geissler "Severity"); 878cb92c03bSAndrew Geissler } 879cb92c03bSAndrew Geissler } 880cb92c03bSAndrew Geissler else if (propertyMap.first == "Message") 881cb92c03bSAndrew Geissler { 882cb92c03bSAndrew Geissler message = std::get_if<std::string>(&propertyMap.second); 883cb92c03bSAndrew Geissler if (message == nullptr) 884cb92c03bSAndrew Geissler { 885cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 886cb92c03bSAndrew Geissler "Message"); 887cb92c03bSAndrew Geissler } 888cb92c03bSAndrew Geissler } 889cb92c03bSAndrew Geissler } 890cb92c03bSAndrew Geissler asyncResp->res.jsonValue = { 891cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 892cb92c03bSAndrew Geissler {"@odata.context", "/redfish/v1/" 893cb92c03bSAndrew Geissler "$metadata#LogEntry.LogEntry"}, 894cb92c03bSAndrew Geissler {"@odata.id", 895cb92c03bSAndrew Geissler "/redfish/v1/Systems/system/LogServices/EventLog/" 896cb92c03bSAndrew Geissler "Entries/" + 897cb92c03bSAndrew Geissler std::to_string(*id)}, 898cb92c03bSAndrew Geissler {"Name", "System DBus Event Log Entry"}, 899cb92c03bSAndrew Geissler {"Id", std::to_string(*id)}, 900cb92c03bSAndrew Geissler {"Message", *message}, 901cb92c03bSAndrew Geissler {"EntryType", "Event"}, 902cb92c03bSAndrew Geissler {"Severity", translateSeverityDbusToRedfish(*severity)}, 903cb92c03bSAndrew Geissler {"Created", crow::utility::getDateTime(timestamp)}}; 904cb92c03bSAndrew Geissler }, 905cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging", 906cb92c03bSAndrew Geissler "/xyz/openbmc_project/logging/entry/" + entryID, 907cb92c03bSAndrew Geissler "org.freedesktop.DBus.Properties", "GetAll", 908cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging.Entry"); 909cb92c03bSAndrew Geissler #endif 910c4bf6374SJason M. Bills } 911c4bf6374SJason M. Bills }; 912c4bf6374SJason M. Bills 913c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node 914c4bf6374SJason M. Bills { 915c4bf6374SJason M. Bills public: 916c4bf6374SJason M. Bills template <typename CrowApp> 917c4bf6374SJason M. Bills BMCLogServiceCollection(CrowApp &app) : 9184ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/") 9191da66f75SEd Tanous { 9201da66f75SEd Tanous entityPrivileges = { 921e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 922e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 923e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 924e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 925e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 926e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 9271da66f75SEd Tanous } 9281da66f75SEd Tanous 9291da66f75SEd Tanous private: 9301da66f75SEd Tanous /** 9311da66f75SEd Tanous * Functions triggers appropriate requests on DBus 9321da66f75SEd Tanous */ 9331da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 9341da66f75SEd Tanous const std::vector<std::string> ¶ms) override 9351da66f75SEd Tanous { 936e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 9371da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 9381da66f75SEd Tanous // it has a duplicate entry for members 939e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 9401da66f75SEd Tanous "#LogServiceCollection.LogServiceCollection"; 941e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 942c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection"; 943e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 944e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices"; 945e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection"; 946e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 9471da66f75SEd Tanous "Collection of LogServices for this Manager"; 948c4bf6374SJason M. Bills nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; 949c4bf6374SJason M. Bills logServiceArray = nlohmann::json::array(); 950c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL 951c4bf6374SJason M. Bills logServiceArray.push_back( 952cb92c03bSAndrew Geissler {{ "@odata.id", 953cb92c03bSAndrew Geissler "/redfish/v1/Managers/bmc/LogServices/Journal" }}); 954c4bf6374SJason M. Bills #endif 955e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 956c4bf6374SJason M. Bills logServiceArray.size(); 9571da66f75SEd Tanous } 9581da66f75SEd Tanous }; 9591da66f75SEd Tanous 960c4bf6374SJason M. Bills class BMCJournalLogService : public Node 9611da66f75SEd Tanous { 9621da66f75SEd Tanous public: 9631da66f75SEd Tanous template <typename CrowApp> 964c4bf6374SJason M. Bills BMCJournalLogService(CrowApp &app) : 965c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/") 966e1f26343SJason M. Bills { 967e1f26343SJason M. Bills entityPrivileges = { 968e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 969e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 970e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 971e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 972e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 973e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 974e1f26343SJason M. Bills } 975e1f26343SJason M. Bills 976e1f26343SJason M. Bills private: 977e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 978e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 979e1f26343SJason M. Bills { 980e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 981e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 982e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 9830f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 9840f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal"; 985e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 986e1f26343SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 987c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service"; 988c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service"; 989c4bf6374SJason M. Bills asyncResp->res.jsonValue["Id"] = "BMC Journal"; 990e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 991cd50aa42SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 992cd50aa42SJason M. Bills {"@odata.id", 993cd50aa42SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/"}}; 994e1f26343SJason M. Bills } 995e1f26343SJason M. Bills }; 996e1f26343SJason M. Bills 997c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID, 998e1f26343SJason M. Bills sd_journal *journal, 999c4bf6374SJason M. Bills nlohmann::json &bmcJournalLogEntryJson) 1000e1f26343SJason M. Bills { 1001e1f26343SJason M. Bills // Get the Log Entry contents 1002e1f26343SJason M. Bills int ret = 0; 1003e1f26343SJason M. Bills 100439e77504SEd Tanous std::string_view msg; 100516428a1aSJason M. Bills ret = getJournalMetadata(journal, "MESSAGE", msg); 1006e1f26343SJason M. Bills if (ret < 0) 1007e1f26343SJason M. Bills { 1008e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret); 1009e1f26343SJason M. Bills return 1; 1010e1f26343SJason M. Bills } 1011e1f26343SJason M. Bills 1012e1f26343SJason M. Bills // Get the severity from the PRIORITY field 1013e1f26343SJason M. Bills int severity = 8; // Default to an invalid priority 101416428a1aSJason M. Bills ret = getJournalMetadata(journal, "PRIORITY", 10, severity); 1015e1f26343SJason M. Bills if (ret < 0) 1016e1f26343SJason M. Bills { 1017e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret); 1018e1f26343SJason M. Bills return 1; 1019e1f26343SJason M. Bills } 1020e1f26343SJason M. Bills 1021e1f26343SJason M. Bills // Get the Created time from the timestamp 102216428a1aSJason M. Bills std::string entryTimeStr; 102316428a1aSJason M. Bills if (!getEntryTimestamp(journal, entryTimeStr)) 1024e1f26343SJason M. Bills { 102516428a1aSJason M. Bills return 1; 1026e1f26343SJason M. Bills } 1027e1f26343SJason M. Bills 1028e1f26343SJason M. Bills // Fill in the log entry with the gathered data 1029c4bf6374SJason M. Bills bmcJournalLogEntryJson = { 1030cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 1031e1f26343SJason M. Bills {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 1032c4bf6374SJason M. Bills {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" + 1033c4bf6374SJason M. Bills bmcJournalLogEntryID}, 1034e1f26343SJason M. Bills {"Name", "BMC Journal Entry"}, 1035c4bf6374SJason M. Bills {"Id", bmcJournalLogEntryID}, 103616428a1aSJason M. Bills {"Message", msg}, 1037e1f26343SJason M. Bills {"EntryType", "Oem"}, 1038e1f26343SJason M. Bills {"Severity", 1039e1f26343SJason M. Bills severity <= 2 ? "Critical" 1040e1f26343SJason M. Bills : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""}, 1041e1f26343SJason M. Bills {"OemRecordFormat", "Intel BMC Journal Entry"}, 1042e1f26343SJason M. Bills {"Created", std::move(entryTimeStr)}}; 1043e1f26343SJason M. Bills return 0; 1044e1f26343SJason M. Bills } 1045e1f26343SJason M. Bills 1046c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node 1047e1f26343SJason M. Bills { 1048e1f26343SJason M. Bills public: 1049e1f26343SJason M. Bills template <typename CrowApp> 1050c4bf6374SJason M. Bills BMCJournalLogEntryCollection(CrowApp &app) : 1051c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/") 1052e1f26343SJason M. Bills { 1053e1f26343SJason M. Bills entityPrivileges = { 1054e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1055e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1056e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1057e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1058e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1059e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1060e1f26343SJason M. Bills } 1061e1f26343SJason M. Bills 1062e1f26343SJason M. Bills private: 1063e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1064e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1065e1f26343SJason M. Bills { 1066e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1067193ad2faSJason M. Bills static constexpr const long maxEntriesPerPage = 1000; 1068193ad2faSJason M. Bills long skip = 0; 1069193ad2faSJason M. Bills long top = maxEntriesPerPage; // Show max entries by default 107016428a1aSJason M. Bills if (!getSkipParam(asyncResp->res, req, skip)) 1071193ad2faSJason M. Bills { 1072193ad2faSJason M. Bills return; 1073193ad2faSJason M. Bills } 107416428a1aSJason M. Bills if (!getTopParam(asyncResp->res, req, top)) 1075193ad2faSJason M. Bills { 1076193ad2faSJason M. Bills return; 1077193ad2faSJason M. Bills } 1078e1f26343SJason M. Bills // Collections don't include the static data added by SubRoute because 1079e1f26343SJason M. Bills // it has a duplicate entry for members 1080e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1081e1f26343SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 10820f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 10830f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; 1084e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1085c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection"; 1086e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 1087c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; 1088e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; 1089e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 1090e1f26343SJason M. Bills "Collection of BMC Journal Entries"; 10910f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 10920f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; 1093e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 1094e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 1095e1f26343SJason M. Bills 1096e1f26343SJason M. Bills // Go through the journal and use the timestamp to create a unique ID 1097e1f26343SJason M. Bills // for each entry 1098e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 1099e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 1100e1f26343SJason M. Bills if (ret < 0) 1101e1f26343SJason M. Bills { 1102e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 1103f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1104e1f26343SJason M. Bills return; 1105e1f26343SJason M. Bills } 1106e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 1107e1f26343SJason M. Bills journalTmp, sd_journal_close); 1108e1f26343SJason M. Bills journalTmp = nullptr; 1109b01bf299SEd Tanous uint64_t entryCount = 0; 1110e1f26343SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 1111e1f26343SJason M. Bills { 1112193ad2faSJason M. Bills entryCount++; 1113193ad2faSJason M. Bills // Handle paging using skip (number of entries to skip from the 1114193ad2faSJason M. Bills // start) and top (number of entries to display) 1115193ad2faSJason M. Bills if (entryCount <= skip || entryCount > skip + top) 1116193ad2faSJason M. Bills { 1117193ad2faSJason M. Bills continue; 1118193ad2faSJason M. Bills } 1119193ad2faSJason M. Bills 112016428a1aSJason M. Bills std::string idStr; 112116428a1aSJason M. Bills if (!getUniqueEntryID(journal.get(), idStr)) 1122e1f26343SJason M. Bills { 1123e1f26343SJason M. Bills continue; 1124e1f26343SJason M. Bills } 1125e1f26343SJason M. Bills 1126e1f26343SJason M. Bills logEntryArray.push_back({}); 1127c4bf6374SJason M. Bills nlohmann::json &bmcJournalLogEntry = logEntryArray.back(); 1128c4bf6374SJason M. Bills if (fillBMCJournalLogEntryJson(idStr, journal.get(), 1129c4bf6374SJason M. Bills bmcJournalLogEntry) != 0) 1130e1f26343SJason M. Bills { 1131f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1132e1f26343SJason M. Bills return; 1133e1f26343SJason M. Bills } 1134e1f26343SJason M. Bills } 1135193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 1136193ad2faSJason M. Bills if (skip + top < entryCount) 1137193ad2faSJason M. Bills { 1138193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 1139c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" + 1140193ad2faSJason M. Bills std::to_string(skip + top); 1141193ad2faSJason M. Bills } 1142e1f26343SJason M. Bills } 1143e1f26343SJason M. Bills }; 1144e1f26343SJason M. Bills 1145c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node 1146e1f26343SJason M. Bills { 1147e1f26343SJason M. Bills public: 1148c4bf6374SJason M. Bills BMCJournalLogEntry(CrowApp &app) : 1149c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/", 1150e1f26343SJason M. Bills std::string()) 1151e1f26343SJason M. Bills { 1152e1f26343SJason M. Bills entityPrivileges = { 1153e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1154e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1155e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1156e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1157e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1158e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1159e1f26343SJason M. Bills } 1160e1f26343SJason M. Bills 1161e1f26343SJason M. Bills private: 1162e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1163e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1164e1f26343SJason M. Bills { 1165e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1166e1f26343SJason M. Bills if (params.size() != 1) 1167e1f26343SJason M. Bills { 1168f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1169e1f26343SJason M. Bills return; 1170e1f26343SJason M. Bills } 117116428a1aSJason M. Bills const std::string &entryID = params[0]; 1172e1f26343SJason M. Bills // Convert the unique ID back to a timestamp to find the entry 1173e1f26343SJason M. Bills uint64_t ts = 0; 1174e1f26343SJason M. Bills uint16_t index = 0; 117516428a1aSJason M. Bills if (!getTimestampFromID(asyncResp->res, entryID, ts, index)) 1176e1f26343SJason M. Bills { 117716428a1aSJason M. Bills return; 1178e1f26343SJason M. Bills } 1179e1f26343SJason M. Bills 1180e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 1181e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 1182e1f26343SJason M. Bills if (ret < 0) 1183e1f26343SJason M. Bills { 1184e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 1185f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1186e1f26343SJason M. Bills return; 1187e1f26343SJason M. Bills } 1188e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 1189e1f26343SJason M. Bills journalTmp, sd_journal_close); 1190e1f26343SJason M. Bills journalTmp = nullptr; 1191e1f26343SJason M. Bills // Go to the timestamp in the log and move to the entry at the index 1192e1f26343SJason M. Bills ret = sd_journal_seek_realtime_usec(journal.get(), ts); 1193e1f26343SJason M. Bills for (int i = 0; i <= index; i++) 1194e1f26343SJason M. Bills { 1195e1f26343SJason M. Bills sd_journal_next(journal.get()); 1196e1f26343SJason M. Bills } 1197c4bf6374SJason M. Bills // Confirm that the entry ID matches what was requested 1198c4bf6374SJason M. Bills std::string idStr; 1199c4bf6374SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID) 1200c4bf6374SJason M. Bills { 1201c4bf6374SJason M. Bills messages::resourceMissingAtURI(asyncResp->res, entryID); 1202c4bf6374SJason M. Bills return; 1203c4bf6374SJason M. Bills } 1204c4bf6374SJason M. Bills 1205c4bf6374SJason M. Bills if (fillBMCJournalLogEntryJson(entryID, journal.get(), 1206e1f26343SJason M. Bills asyncResp->res.jsonValue) != 0) 1207e1f26343SJason M. Bills { 1208f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1209e1f26343SJason M. Bills return; 1210e1f26343SJason M. Bills } 1211e1f26343SJason M. Bills } 1212e1f26343SJason M. Bills }; 1213e1f26343SJason M. Bills 1214424c4176SJason M. Bills class CrashdumpService : public Node 1215e1f26343SJason M. Bills { 1216e1f26343SJason M. Bills public: 1217e1f26343SJason M. Bills template <typename CrowApp> 1218424c4176SJason M. Bills CrashdumpService(CrowApp &app) : 1219424c4176SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/") 12201da66f75SEd Tanous { 12211da66f75SEd Tanous entityPrivileges = { 1222e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1223e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1224e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1225e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1226e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1227e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 12281da66f75SEd Tanous } 12291da66f75SEd Tanous 12301da66f75SEd Tanous private: 12311da66f75SEd Tanous /** 12321da66f75SEd Tanous * Functions triggers appropriate requests on DBus 12331da66f75SEd Tanous */ 12341da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 12351da66f75SEd Tanous const std::vector<std::string> ¶ms) override 12361da66f75SEd Tanous { 1237e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 12381da66f75SEd Tanous // Copy over the static data to include the entries added by SubRoute 12390f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 1240424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump"; 1241e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1242e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 1243e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1244c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 1245424c4176SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Service"; 1246424c4176SJason M. Bills asyncResp->res.jsonValue["Description"] = "Crashdump Service"; 1247424c4176SJason M. Bills asyncResp->res.jsonValue["Id"] = "Crashdump"; 1248e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 1249e1f26343SJason M. Bills asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 1250cd50aa42SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 1251cd50aa42SJason M. Bills {"@odata.id", 1252424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}}; 1253e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"] = { 12541da66f75SEd Tanous {"Oem", 1255424c4176SJason M. Bills {{"#Crashdump.OnDemand", 1256424c4176SJason M. Bills {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/" 1257424c4176SJason M. Bills "Actions/Oem/Crashdump.OnDemand"}}}}}}; 12581da66f75SEd Tanous 12591da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI 1260e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"]["Oem"].push_back( 1261424c4176SJason M. Bills {"#Crashdump.SendRawPeci", 1262cb92c03bSAndrew Geissler { { "target", 1263424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/" 1264424c4176SJason M. Bills "Actions/Oem/Crashdump.SendRawPeci" } }}); 12651da66f75SEd Tanous #endif 12661da66f75SEd Tanous } 12671da66f75SEd Tanous }; 12681da66f75SEd Tanous 1269424c4176SJason M. Bills class CrashdumpEntryCollection : public Node 12701da66f75SEd Tanous { 12711da66f75SEd Tanous public: 12721da66f75SEd Tanous template <typename CrowApp> 1273424c4176SJason M. Bills CrashdumpEntryCollection(CrowApp &app) : 1274424c4176SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/") 12751da66f75SEd Tanous { 12761da66f75SEd Tanous entityPrivileges = { 1277e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1278e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1279e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1280e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1281e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1282e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 12831da66f75SEd Tanous } 12841da66f75SEd Tanous 12851da66f75SEd Tanous private: 12861da66f75SEd Tanous /** 12871da66f75SEd Tanous * Functions triggers appropriate requests on DBus 12881da66f75SEd Tanous */ 12891da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 12901da66f75SEd Tanous const std::vector<std::string> ¶ms) override 12911da66f75SEd Tanous { 1292e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 12931da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 12941da66f75SEd Tanous // it has a duplicate entry for members 1295e1f26343SJason M. Bills auto getLogEntriesCallback = [asyncResp]( 1296e1f26343SJason M. Bills const boost::system::error_code ec, 12971da66f75SEd Tanous const std::vector<std::string> &resp) { 12981da66f75SEd Tanous if (ec) 12991da66f75SEd Tanous { 13001da66f75SEd Tanous if (ec.value() != 13011da66f75SEd Tanous boost::system::errc::no_such_file_or_directory) 13021da66f75SEd Tanous { 13031da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to get entries ec: " 13041da66f75SEd Tanous << ec.message(); 1305f12894f8SJason M. Bills messages::internalError(asyncResp->res); 13061da66f75SEd Tanous return; 13071da66f75SEd Tanous } 13081da66f75SEd Tanous } 1309e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 13101da66f75SEd Tanous "#LogEntryCollection.LogEntryCollection"; 13110f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 1312424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"; 1313e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1314d53dd41fSJason M. Bills "/redfish/v1/" 1315d53dd41fSJason M. Bills "$metadata#LogEntryCollection.LogEntryCollection"; 1316424c4176SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries"; 1317e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 1318424c4176SJason M. Bills "Collection of Crashdump Entries"; 1319e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 1320e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 13211da66f75SEd Tanous for (const std::string &objpath : resp) 13221da66f75SEd Tanous { 132348e4639eSJason M. Bills // Don't list the on-demand log 1324424c4176SJason M. Bills if (objpath.compare(CrashdumpOnDemandPath) == 0) 13251da66f75SEd Tanous { 13261da66f75SEd Tanous continue; 13271da66f75SEd Tanous } 13284ed77cd5SEd Tanous std::size_t lastPos = objpath.rfind("/"); 13294ed77cd5SEd Tanous if (lastPos != std::string::npos) 13301da66f75SEd Tanous { 1331e1f26343SJason M. Bills logEntryArray.push_back( 1332d53dd41fSJason M. Bills {{"@odata.id", "/redfish/v1/Systems/system/LogServices/" 1333424c4176SJason M. Bills "Crashdump/Entries/" + 13344ed77cd5SEd Tanous objpath.substr(lastPos + 1)}}); 13351da66f75SEd Tanous } 13361da66f75SEd Tanous } 1337e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 1338e1f26343SJason M. Bills logEntryArray.size(); 13391da66f75SEd Tanous }; 13401da66f75SEd Tanous crow::connections::systemBus->async_method_call( 13411da66f75SEd Tanous std::move(getLogEntriesCallback), 13421da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", 13431da66f75SEd Tanous "/xyz/openbmc_project/object_mapper", 13441da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0, 1345424c4176SJason M. Bills std::array<const char *, 1>{CrashdumpInterface}); 13461da66f75SEd Tanous } 13471da66f75SEd Tanous }; 13481da66f75SEd Tanous 1349424c4176SJason M. Bills std::string getLogCreatedTime(const nlohmann::json &Crashdump) 13501da66f75SEd Tanous { 1351424c4176SJason M. Bills nlohmann::json::const_iterator cdIt = Crashdump.find("crashlog_data"); 1352424c4176SJason M. Bills if (cdIt != Crashdump.end()) 13531da66f75SEd Tanous { 1354c4d00437SJason M. Bills nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO"); 1355c4d00437SJason M. Bills if (siIt != cdIt->end()) 13561da66f75SEd Tanous { 1357c4d00437SJason M. Bills nlohmann::json::const_iterator tsIt = siIt->find("timestamp"); 1358c4d00437SJason M. Bills if (tsIt != siIt->end()) 1359c4d00437SJason M. Bills { 1360c4d00437SJason M. Bills const std::string *logTime = 1361c4d00437SJason M. Bills tsIt->get_ptr<const std::string *>(); 13621da66f75SEd Tanous if (logTime != nullptr) 13631da66f75SEd Tanous { 13641da66f75SEd Tanous return *logTime; 13651da66f75SEd Tanous } 13661da66f75SEd Tanous } 13671da66f75SEd Tanous } 1368c4d00437SJason M. Bills } 13691da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to find log timestamp"; 13701da66f75SEd Tanous 13711da66f75SEd Tanous return std::string(); 13721da66f75SEd Tanous } 13731da66f75SEd Tanous 1374424c4176SJason M. Bills class CrashdumpEntry : public Node 13751da66f75SEd Tanous { 13761da66f75SEd Tanous public: 1377424c4176SJason M. Bills CrashdumpEntry(CrowApp &app) : 1378d53dd41fSJason M. Bills Node(app, 1379424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/", 13801da66f75SEd Tanous std::string()) 13811da66f75SEd Tanous { 13821da66f75SEd Tanous entityPrivileges = { 1383e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1384e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1385e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1386e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1387e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1388e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 13891da66f75SEd Tanous } 13901da66f75SEd Tanous 13911da66f75SEd Tanous private: 13921da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 13931da66f75SEd Tanous const std::vector<std::string> ¶ms) override 13941da66f75SEd Tanous { 1395e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 13961da66f75SEd Tanous if (params.size() != 1) 13971da66f75SEd Tanous { 1398f12894f8SJason M. Bills messages::internalError(asyncResp->res); 13991da66f75SEd Tanous return; 14001da66f75SEd Tanous } 1401b01bf299SEd Tanous const uint8_t logId = std::atoi(params[0].c_str()); 1402abf2add6SEd Tanous auto getStoredLogCallback = [asyncResp, logId]( 1403abf2add6SEd Tanous const boost::system::error_code ec, 1404abf2add6SEd Tanous const std::variant<std::string> &resp) { 14051da66f75SEd Tanous if (ec) 14061da66f75SEd Tanous { 1407abf2add6SEd Tanous BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message(); 1408f12894f8SJason M. Bills messages::internalError(asyncResp->res); 14091da66f75SEd Tanous return; 14101da66f75SEd Tanous } 1411abf2add6SEd Tanous const std::string *log = std::get_if<std::string>(&resp); 14121da66f75SEd Tanous if (log == nullptr) 14131da66f75SEd Tanous { 1414f12894f8SJason M. Bills messages::internalError(asyncResp->res); 14151da66f75SEd Tanous return; 14161da66f75SEd Tanous } 14171da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 14181da66f75SEd Tanous if (j.is_discarded()) 14191da66f75SEd Tanous { 1420f12894f8SJason M. Bills messages::internalError(asyncResp->res); 14211da66f75SEd Tanous return; 14221da66f75SEd Tanous } 14231da66f75SEd Tanous std::string t = getLogCreatedTime(j); 1424e1f26343SJason M. Bills asyncResp->res.jsonValue = { 1425cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 1426abf2add6SEd Tanous {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 14271da66f75SEd Tanous {"@odata.id", 1428424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + 14294ed77cd5SEd Tanous std::to_string(logId)}, 1430424c4176SJason M. Bills {"Name", "CPU Crashdump"}, 14314ed77cd5SEd Tanous {"Id", logId}, 14321da66f75SEd Tanous {"EntryType", "Oem"}, 1433424c4176SJason M. Bills {"OemRecordFormat", "Intel Crashdump"}, 14341da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 14351da66f75SEd Tanous {"Created", std::move(t)}}; 14361da66f75SEd Tanous }; 14371da66f75SEd Tanous crow::connections::systemBus->async_method_call( 1438424c4176SJason M. Bills std::move(getStoredLogCallback), CrashdumpObject, 1439424c4176SJason M. Bills CrashdumpPath + std::string("/") + std::to_string(logId), 1440424c4176SJason M. Bills "org.freedesktop.DBus.Properties", "Get", CrashdumpInterface, 1441424c4176SJason M. Bills "Log"); 14421da66f75SEd Tanous } 14431da66f75SEd Tanous }; 14441da66f75SEd Tanous 1445424c4176SJason M. Bills class OnDemandCrashdump : public Node 14461da66f75SEd Tanous { 14471da66f75SEd Tanous public: 1448424c4176SJason M. Bills OnDemandCrashdump(CrowApp &app) : 1449424c4176SJason M. Bills Node(app, 1450424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/" 1451424c4176SJason M. Bills "Crashdump.OnDemand/") 14521da66f75SEd Tanous { 14531da66f75SEd Tanous entityPrivileges = { 1454e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1455e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1456e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1457e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1458e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1459e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 14601da66f75SEd Tanous } 14611da66f75SEd Tanous 14621da66f75SEd Tanous private: 14631da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 14641da66f75SEd Tanous const std::vector<std::string> ¶ms) override 14651da66f75SEd Tanous { 1466e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 146748e4639eSJason M. Bills static std::unique_ptr<sdbusplus::bus::match::match> onDemandLogMatcher; 14681da66f75SEd Tanous 146948e4639eSJason M. Bills // Only allow one OnDemand Log request at a time 147048e4639eSJason M. Bills if (onDemandLogMatcher != nullptr) 14711da66f75SEd Tanous { 1472e1f26343SJason M. Bills asyncResp->res.addHeader("Retry-After", "30"); 1473f12894f8SJason M. Bills messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); 14741da66f75SEd Tanous return; 14751da66f75SEd Tanous } 14761da66f75SEd Tanous // Make this static so it survives outside this method 14771da66f75SEd Tanous static boost::asio::deadline_timer timeout(*req.ioService); 14781da66f75SEd Tanous 14791da66f75SEd Tanous timeout.expires_from_now(boost::posix_time::seconds(30)); 1480e1f26343SJason M. Bills timeout.async_wait([asyncResp](const boost::system::error_code &ec) { 148148e4639eSJason M. Bills onDemandLogMatcher = nullptr; 14821da66f75SEd Tanous if (ec) 14831da66f75SEd Tanous { 14841da66f75SEd Tanous // operation_aborted is expected if timer is canceled before 14851da66f75SEd Tanous // completion. 14861da66f75SEd Tanous if (ec != boost::asio::error::operation_aborted) 14871da66f75SEd Tanous { 14881da66f75SEd Tanous BMCWEB_LOG_ERROR << "Async_wait failed " << ec; 14891da66f75SEd Tanous } 14901da66f75SEd Tanous return; 14911da66f75SEd Tanous } 149248e4639eSJason M. Bills BMCWEB_LOG_ERROR << "Timed out waiting for on-demand log"; 14931da66f75SEd Tanous 1494f12894f8SJason M. Bills messages::internalError(asyncResp->res); 14951da66f75SEd Tanous }); 14961da66f75SEd Tanous 149748e4639eSJason M. Bills auto onDemandLogMatcherCallback = [asyncResp]( 14981da66f75SEd Tanous sdbusplus::message::message &m) { 149948e4639eSJason M. Bills BMCWEB_LOG_DEBUG << "OnDemand log available match fired"; 15001da66f75SEd Tanous boost::system::error_code ec; 15011da66f75SEd Tanous timeout.cancel(ec); 15021da66f75SEd Tanous if (ec) 15031da66f75SEd Tanous { 15041da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " << ec; 15051da66f75SEd Tanous } 15064ed77cd5SEd Tanous sdbusplus::message::object_path objPath; 15071da66f75SEd Tanous boost::container::flat_map< 1508abf2add6SEd Tanous std::string, boost::container::flat_map< 1509abf2add6SEd Tanous std::string, std::variant<std::string>>> 15104ed77cd5SEd Tanous interfacesAdded; 15114ed77cd5SEd Tanous m.read(objPath, interfacesAdded); 1512abf2add6SEd Tanous const std::string *log = std::get_if<std::string>( 1513424c4176SJason M. Bills &interfacesAdded[CrashdumpInterface]["Log"]); 15141da66f75SEd Tanous if (log == nullptr) 15151da66f75SEd Tanous { 1516f12894f8SJason M. Bills messages::internalError(asyncResp->res); 151748e4639eSJason M. Bills // Careful with onDemandLogMatcher. It is a unique_ptr to the 15181da66f75SEd Tanous // match object inside which this lambda is executing. Once it 15191da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 15201da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 15211da66f75SEd Tanous // be the last thing done. 152248e4639eSJason M. Bills onDemandLogMatcher = nullptr; 15231da66f75SEd Tanous return; 15241da66f75SEd Tanous } 15251da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 15261da66f75SEd Tanous if (j.is_discarded()) 15271da66f75SEd Tanous { 1528f12894f8SJason M. Bills messages::internalError(asyncResp->res); 152948e4639eSJason M. Bills // Careful with onDemandLogMatcher. It is a unique_ptr to the 15301da66f75SEd Tanous // match object inside which this lambda is executing. Once it 15311da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 15321da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 15331da66f75SEd Tanous // be the last thing done. 153448e4639eSJason M. Bills onDemandLogMatcher = nullptr; 15351da66f75SEd Tanous return; 15361da66f75SEd Tanous } 15371da66f75SEd Tanous std::string t = getLogCreatedTime(j); 1538e1f26343SJason M. Bills asyncResp->res.jsonValue = { 1539cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 15401da66f75SEd Tanous {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 1541424c4176SJason M. Bills {"Name", "CPU Crashdump"}, 15421da66f75SEd Tanous {"EntryType", "Oem"}, 1543424c4176SJason M. Bills {"OemRecordFormat", "Intel Crashdump"}, 15441da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 15451da66f75SEd Tanous {"Created", std::move(t)}}; 154648e4639eSJason M. Bills // Careful with onDemandLogMatcher. It is a unique_ptr to the 15471da66f75SEd Tanous // match object inside which this lambda is executing. Once it is 15481da66f75SEd Tanous // set to nullptr, the match object will be destroyed and the lambda 15491da66f75SEd Tanous // will lose its context, including res, so it needs to be the last 15501da66f75SEd Tanous // thing done. 155148e4639eSJason M. Bills onDemandLogMatcher = nullptr; 15521da66f75SEd Tanous }; 155348e4639eSJason M. Bills onDemandLogMatcher = std::make_unique<sdbusplus::bus::match::match>( 15541da66f75SEd Tanous *crow::connections::systemBus, 15551da66f75SEd Tanous sdbusplus::bus::match::rules::interfacesAdded() + 1556424c4176SJason M. Bills sdbusplus::bus::match::rules::argNpath(0, 1557424c4176SJason M. Bills CrashdumpOnDemandPath), 155848e4639eSJason M. Bills std::move(onDemandLogMatcherCallback)); 15591da66f75SEd Tanous 156048e4639eSJason M. Bills auto generateonDemandLogCallback = 1561e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 15621da66f75SEd Tanous const std::string &resp) { 15631da66f75SEd Tanous if (ec) 15641da66f75SEd Tanous { 15651da66f75SEd Tanous if (ec.value() == 15661da66f75SEd Tanous boost::system::errc::operation_not_supported) 15671da66f75SEd Tanous { 1568f12894f8SJason M. Bills messages::resourceInStandby(asyncResp->res); 15691da66f75SEd Tanous } 15701da66f75SEd Tanous else 15711da66f75SEd Tanous { 1572f12894f8SJason M. Bills messages::internalError(asyncResp->res); 15731da66f75SEd Tanous } 15741da66f75SEd Tanous boost::system::error_code timeoutec; 15751da66f75SEd Tanous timeout.cancel(timeoutec); 15761da66f75SEd Tanous if (timeoutec) 15771da66f75SEd Tanous { 15781da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " 15791da66f75SEd Tanous << timeoutec; 15801da66f75SEd Tanous } 158148e4639eSJason M. Bills onDemandLogMatcher = nullptr; 15821da66f75SEd Tanous return; 15831da66f75SEd Tanous } 15841da66f75SEd Tanous }; 15851da66f75SEd Tanous crow::connections::systemBus->async_method_call( 1586424c4176SJason M. Bills std::move(generateonDemandLogCallback), CrashdumpObject, 1587424c4176SJason M. Bills CrashdumpPath, CrashdumpOnDemandInterface, "GenerateOnDemandLog"); 15881da66f75SEd Tanous } 15891da66f75SEd Tanous }; 15901da66f75SEd Tanous 1591e1f26343SJason M. Bills class SendRawPECI : public Node 15921da66f75SEd Tanous { 15931da66f75SEd Tanous public: 1594e1f26343SJason M. Bills SendRawPECI(CrowApp &app) : 1595424c4176SJason M. Bills Node(app, 1596424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/" 1597424c4176SJason M. Bills "Crashdump.SendRawPeci/") 15981da66f75SEd Tanous { 15991da66f75SEd Tanous entityPrivileges = { 16001da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 16011da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 16021da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 16031da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 16041da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 16051da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 16061da66f75SEd Tanous } 16071da66f75SEd Tanous 16081da66f75SEd Tanous private: 16091da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 16101da66f75SEd Tanous const std::vector<std::string> ¶ms) override 16111da66f75SEd Tanous { 1612e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1613b1556427SEd Tanous uint8_t clientAddress = 0; 1614b1556427SEd Tanous uint8_t readLength = 0; 16151da66f75SEd Tanous std::vector<uint8_t> peciCommand; 1616b1556427SEd Tanous if (!json_util::readJson(req, res, "ClientAddress", clientAddress, 1617b1556427SEd Tanous "ReadLength", readLength, "PECICommand", 1618b1556427SEd Tanous peciCommand)) 16191da66f75SEd Tanous { 16201da66f75SEd Tanous return; 16211da66f75SEd Tanous } 1622b1556427SEd Tanous 16231da66f75SEd Tanous // Callback to return the Raw PECI response 1624e1f26343SJason M. Bills auto sendRawPECICallback = 1625e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 16261da66f75SEd Tanous const std::vector<uint8_t> &resp) { 16271da66f75SEd Tanous if (ec) 16281da66f75SEd Tanous { 16291da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to send PECI command ec: " 16301da66f75SEd Tanous << ec.message(); 1631f12894f8SJason M. Bills messages::internalError(asyncResp->res); 16321da66f75SEd Tanous return; 16331da66f75SEd Tanous } 1634e1f26343SJason M. Bills asyncResp->res.jsonValue = {{"Name", "PECI Command Response"}, 16351da66f75SEd Tanous {"PECIResponse", resp}}; 16361da66f75SEd Tanous }; 16371da66f75SEd Tanous // Call the SendRawPECI command with the provided data 16381da66f75SEd Tanous crow::connections::systemBus->async_method_call( 1639424c4176SJason M. Bills std::move(sendRawPECICallback), CrashdumpObject, CrashdumpPath, 1640424c4176SJason M. Bills CrashdumpRawPECIInterface, "SendRawPeci", clientAddress, readLength, 16414ed77cd5SEd Tanous peciCommand); 16421da66f75SEd Tanous } 16431da66f75SEd Tanous }; 16441da66f75SEd Tanous 1645cb92c03bSAndrew Geissler /** 1646cb92c03bSAndrew Geissler * DBusLogServiceActionsClear class supports POST method for ClearLog action. 1647cb92c03bSAndrew Geissler */ 1648cb92c03bSAndrew Geissler class DBusLogServiceActionsClear : public Node 1649cb92c03bSAndrew Geissler { 1650cb92c03bSAndrew Geissler public: 1651cb92c03bSAndrew Geissler DBusLogServiceActionsClear(CrowApp &app) : 1652cb92c03bSAndrew Geissler Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/" 1653cb92c03bSAndrew Geissler "LogService.Reset") 1654cb92c03bSAndrew Geissler { 1655cb92c03bSAndrew Geissler entityPrivileges = { 1656cb92c03bSAndrew Geissler {boost::beast::http::verb::get, {{"Login"}}}, 1657cb92c03bSAndrew Geissler {boost::beast::http::verb::head, {{"Login"}}}, 1658cb92c03bSAndrew Geissler {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1659cb92c03bSAndrew Geissler {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1660cb92c03bSAndrew Geissler {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1661cb92c03bSAndrew Geissler {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1662cb92c03bSAndrew Geissler } 1663cb92c03bSAndrew Geissler 1664cb92c03bSAndrew Geissler private: 1665cb92c03bSAndrew Geissler /** 1666cb92c03bSAndrew Geissler * Function handles POST method request. 1667cb92c03bSAndrew Geissler * The Clear Log actions does not require any parameter.The action deletes 1668cb92c03bSAndrew Geissler * all entries found in the Entries collection for this Log Service. 1669cb92c03bSAndrew Geissler */ 1670cb92c03bSAndrew Geissler void doPost(crow::Response &res, const crow::Request &req, 1671cb92c03bSAndrew Geissler const std::vector<std::string> ¶ms) override 1672cb92c03bSAndrew Geissler { 1673cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "Do delete all entries."; 1674cb92c03bSAndrew Geissler 1675cb92c03bSAndrew Geissler auto asyncResp = std::make_shared<AsyncResp>(res); 1676cb92c03bSAndrew Geissler // Process response from Logging service. 1677cb92c03bSAndrew Geissler auto resp_handler = [asyncResp](const boost::system::error_code ec) { 1678cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done"; 1679cb92c03bSAndrew Geissler if (ec) 1680cb92c03bSAndrew Geissler { 1681cb92c03bSAndrew Geissler // TODO Handle for specific error code 1682cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec; 1683cb92c03bSAndrew Geissler asyncResp->res.result( 1684cb92c03bSAndrew Geissler boost::beast::http::status::internal_server_error); 1685cb92c03bSAndrew Geissler return; 1686cb92c03bSAndrew Geissler } 1687cb92c03bSAndrew Geissler 1688cb92c03bSAndrew Geissler asyncResp->res.result(boost::beast::http::status::no_content); 1689cb92c03bSAndrew Geissler }; 1690cb92c03bSAndrew Geissler 1691cb92c03bSAndrew Geissler // Make call to Logging service to request Clear Log 1692cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 1693cb92c03bSAndrew Geissler resp_handler, "xyz.openbmc_project.Logging", 1694cb92c03bSAndrew Geissler "/xyz/openbmc_project/logging", 1695cb92c03bSAndrew Geissler "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); 1696cb92c03bSAndrew Geissler } 1697cb92c03bSAndrew Geissler }; 16981da66f75SEd Tanous } // namespace redfish 1699