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 18f6150403SJames Feist #include "filesystem.hpp" 191da66f75SEd Tanous #include "node.hpp" 201da66f75SEd Tanous 21e1f26343SJason M. Bills #include <systemd/sd-journal.h> 22e1f26343SJason M. Bills 231da66f75SEd Tanous #include <boost/container/flat_map.hpp> 24*cb92c03bSAndrew Geissler #include <error_messages.hpp> 25abf2add6SEd Tanous #include <variant> 261da66f75SEd Tanous 271da66f75SEd Tanous namespace redfish 281da66f75SEd Tanous { 291da66f75SEd Tanous 304ed77cd5SEd Tanous constexpr char const *cpuLogObject = "com.intel.CpuDebugLog"; 314ed77cd5SEd Tanous constexpr char const *cpuLogPath = "/com/intel/CpuDebugLog"; 324ed77cd5SEd Tanous constexpr char const *cpuLogImmediatePath = "/com/intel/CpuDebugLog/Immediate"; 334ed77cd5SEd Tanous constexpr char const *cpuLogInterface = "com.intel.CpuDebugLog"; 344ed77cd5SEd Tanous constexpr char const *cpuLogImmediateInterface = 351da66f75SEd Tanous "com.intel.CpuDebugLog.Immediate"; 36e1f26343SJason M. Bills constexpr char const *cpuLogRawPECIInterface = 371da66f75SEd Tanous "com.intel.CpuDebugLog.SendRawPeci"; 381da66f75SEd Tanous 39f6150403SJames Feist namespace fs = std::filesystem; 401da66f75SEd Tanous 41*cb92c03bSAndrew Geissler using GetManagedPropertyType = boost::container::flat_map< 42*cb92c03bSAndrew Geissler std::string, 43*cb92c03bSAndrew Geissler sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t, 44*cb92c03bSAndrew Geissler int32_t, uint32_t, int64_t, uint64_t, double>>; 45*cb92c03bSAndrew Geissler 46*cb92c03bSAndrew Geissler using GetManagedObjectsType = boost::container::flat_map< 47*cb92c03bSAndrew Geissler sdbusplus::message::object_path, 48*cb92c03bSAndrew Geissler boost::container::flat_map<std::string, GetManagedPropertyType>>; 49*cb92c03bSAndrew Geissler 50*cb92c03bSAndrew Geissler inline std::string translateSeverityDbusToRedfish(const std::string &s) 51*cb92c03bSAndrew Geissler { 52*cb92c03bSAndrew Geissler if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert") 53*cb92c03bSAndrew Geissler { 54*cb92c03bSAndrew Geissler return "Critical"; 55*cb92c03bSAndrew Geissler } 56*cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") 57*cb92c03bSAndrew Geissler { 58*cb92c03bSAndrew Geissler return "Critical"; 59*cb92c03bSAndrew Geissler } 60*cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug") 61*cb92c03bSAndrew Geissler { 62*cb92c03bSAndrew Geissler return "OK"; 63*cb92c03bSAndrew Geissler } 64*cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") 65*cb92c03bSAndrew Geissler { 66*cb92c03bSAndrew Geissler return "Critical"; 67*cb92c03bSAndrew Geissler } 68*cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error") 69*cb92c03bSAndrew Geissler { 70*cb92c03bSAndrew Geissler return "Critical"; 71*cb92c03bSAndrew Geissler } 72*cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") 73*cb92c03bSAndrew Geissler { 74*cb92c03bSAndrew Geissler return "OK"; 75*cb92c03bSAndrew Geissler } 76*cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice") 77*cb92c03bSAndrew Geissler { 78*cb92c03bSAndrew Geissler return "OK"; 79*cb92c03bSAndrew Geissler } 80*cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning") 81*cb92c03bSAndrew Geissler { 82*cb92c03bSAndrew Geissler return "Warning"; 83*cb92c03bSAndrew Geissler } 84*cb92c03bSAndrew Geissler return ""; 85*cb92c03bSAndrew Geissler } 86*cb92c03bSAndrew Geissler 8716428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal, 8839e77504SEd Tanous const std::string_view &field, 8939e77504SEd Tanous std::string_view &contents) 9016428a1aSJason M. Bills { 9116428a1aSJason M. Bills const char *data = nullptr; 9216428a1aSJason M. Bills size_t length = 0; 9316428a1aSJason M. Bills int ret = 0; 9416428a1aSJason M. Bills // Get the metadata from the requested field of the journal entry 95b01bf299SEd Tanous ret = sd_journal_get_data(journal, field.data(), (const void **)&data, 96b01bf299SEd Tanous &length); 9716428a1aSJason M. Bills if (ret < 0) 9816428a1aSJason M. Bills { 9916428a1aSJason M. Bills return ret; 10016428a1aSJason M. Bills } 10139e77504SEd Tanous contents = std::string_view(data, length); 10216428a1aSJason M. Bills // Only use the content after the "=" character. 10316428a1aSJason M. Bills contents.remove_prefix(std::min(contents.find("=") + 1, contents.size())); 10416428a1aSJason M. Bills return ret; 10516428a1aSJason M. Bills } 10616428a1aSJason M. Bills 10716428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal, 10839e77504SEd Tanous const std::string_view &field, const int &base, 10916428a1aSJason M. Bills int &contents) 11016428a1aSJason M. Bills { 11116428a1aSJason M. Bills int ret = 0; 11239e77504SEd Tanous std::string_view metadata; 11316428a1aSJason M. Bills // Get the metadata from the requested field of the journal entry 11416428a1aSJason M. Bills ret = getJournalMetadata(journal, field, metadata); 11516428a1aSJason M. Bills if (ret < 0) 11616428a1aSJason M. Bills { 11716428a1aSJason M. Bills return ret; 11816428a1aSJason M. Bills } 119b01bf299SEd Tanous contents = strtol(metadata.data(), nullptr, base); 12016428a1aSJason M. Bills return ret; 12116428a1aSJason M. Bills } 12216428a1aSJason M. Bills 12316428a1aSJason M. Bills static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp) 12416428a1aSJason M. Bills { 12516428a1aSJason M. Bills int ret = 0; 12616428a1aSJason M. Bills uint64_t timestamp = 0; 12716428a1aSJason M. Bills ret = sd_journal_get_realtime_usec(journal, ×tamp); 12816428a1aSJason M. Bills if (ret < 0) 12916428a1aSJason M. Bills { 13016428a1aSJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 13116428a1aSJason M. Bills << strerror(-ret); 13216428a1aSJason M. Bills return false; 13316428a1aSJason M. Bills } 13416428a1aSJason M. Bills time_t t = 13516428a1aSJason M. Bills static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s 13616428a1aSJason M. Bills struct tm *loctime = localtime(&t); 13716428a1aSJason M. Bills char entryTime[64] = {}; 13816428a1aSJason M. Bills if (NULL != loctime) 13916428a1aSJason M. Bills { 14016428a1aSJason M. Bills strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime); 14116428a1aSJason M. Bills } 14216428a1aSJason M. Bills // Insert the ':' into the timezone 14339e77504SEd Tanous std::string_view t1(entryTime); 14439e77504SEd Tanous std::string_view t2(entryTime); 14516428a1aSJason M. Bills if (t1.size() > 2 && t2.size() > 2) 14616428a1aSJason M. Bills { 14716428a1aSJason M. Bills t1.remove_suffix(2); 14816428a1aSJason M. Bills t2.remove_prefix(t2.size() - 2); 14916428a1aSJason M. Bills } 15039e77504SEd Tanous entryTimestamp = std::string(t1) + ":" + std::string(t2); 15116428a1aSJason M. Bills return true; 15216428a1aSJason M. Bills } 15316428a1aSJason M. Bills 15416428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req, 15516428a1aSJason M. Bills long &skip) 15616428a1aSJason M. Bills { 15716428a1aSJason M. Bills char *skipParam = req.urlParams.get("$skip"); 15816428a1aSJason M. Bills if (skipParam != nullptr) 15916428a1aSJason M. Bills { 16016428a1aSJason M. Bills char *ptr = nullptr; 16116428a1aSJason M. Bills skip = std::strtol(skipParam, &ptr, 10); 16216428a1aSJason M. Bills if (*skipParam == '\0' || *ptr != '\0') 16316428a1aSJason M. Bills { 16416428a1aSJason M. Bills 16516428a1aSJason M. Bills messages::queryParameterValueTypeError(res, std::string(skipParam), 16616428a1aSJason M. Bills "$skip"); 16716428a1aSJason M. Bills return false; 16816428a1aSJason M. Bills } 16916428a1aSJason M. Bills if (skip < 0) 17016428a1aSJason M. Bills { 17116428a1aSJason M. Bills 17216428a1aSJason M. Bills messages::queryParameterOutOfRange(res, std::to_string(skip), 17316428a1aSJason M. Bills "$skip", "greater than 0"); 17416428a1aSJason M. Bills return false; 17516428a1aSJason M. Bills } 17616428a1aSJason M. Bills } 17716428a1aSJason M. Bills return true; 17816428a1aSJason M. Bills } 17916428a1aSJason M. Bills 18016428a1aSJason M. Bills static constexpr const long maxEntriesPerPage = 1000; 18116428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req, 18216428a1aSJason M. Bills long &top) 18316428a1aSJason M. Bills { 18416428a1aSJason M. Bills char *topParam = req.urlParams.get("$top"); 18516428a1aSJason M. Bills if (topParam != nullptr) 18616428a1aSJason M. Bills { 18716428a1aSJason M. Bills char *ptr = nullptr; 18816428a1aSJason M. Bills top = std::strtol(topParam, &ptr, 10); 18916428a1aSJason M. Bills if (*topParam == '\0' || *ptr != '\0') 19016428a1aSJason M. Bills { 19116428a1aSJason M. Bills messages::queryParameterValueTypeError(res, std::string(topParam), 19216428a1aSJason M. Bills "$top"); 19316428a1aSJason M. Bills return false; 19416428a1aSJason M. Bills } 19516428a1aSJason M. Bills if (top < 1 || top > maxEntriesPerPage) 19616428a1aSJason M. Bills { 19716428a1aSJason M. Bills 19816428a1aSJason M. Bills messages::queryParameterOutOfRange( 19916428a1aSJason M. Bills res, std::to_string(top), "$top", 20016428a1aSJason M. Bills "1-" + std::to_string(maxEntriesPerPage)); 20116428a1aSJason M. Bills return false; 20216428a1aSJason M. Bills } 20316428a1aSJason M. Bills } 20416428a1aSJason M. Bills return true; 20516428a1aSJason M. Bills } 20616428a1aSJason M. Bills 20716428a1aSJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID) 20816428a1aSJason M. Bills { 20916428a1aSJason M. Bills int ret = 0; 21016428a1aSJason M. Bills static uint64_t prevTs = 0; 21116428a1aSJason M. Bills static int index = 0; 21216428a1aSJason M. Bills // Get the entry timestamp 21316428a1aSJason M. Bills uint64_t curTs = 0; 21416428a1aSJason M. Bills ret = sd_journal_get_realtime_usec(journal, &curTs); 21516428a1aSJason M. Bills if (ret < 0) 21616428a1aSJason M. Bills { 21716428a1aSJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 21816428a1aSJason M. Bills << strerror(-ret); 21916428a1aSJason M. Bills return false; 22016428a1aSJason M. Bills } 22116428a1aSJason M. Bills // If the timestamp isn't unique, increment the index 22216428a1aSJason M. Bills if (curTs == prevTs) 22316428a1aSJason M. Bills { 22416428a1aSJason M. Bills index++; 22516428a1aSJason M. Bills } 22616428a1aSJason M. Bills else 22716428a1aSJason M. Bills { 22816428a1aSJason M. Bills // Otherwise, reset it 22916428a1aSJason M. Bills index = 0; 23016428a1aSJason M. Bills } 23116428a1aSJason M. Bills // Save the timestamp 23216428a1aSJason M. Bills prevTs = curTs; 23316428a1aSJason M. Bills 23416428a1aSJason M. Bills entryID = std::to_string(curTs); 23516428a1aSJason M. Bills if (index > 0) 23616428a1aSJason M. Bills { 23716428a1aSJason M. Bills entryID += "_" + std::to_string(index); 23816428a1aSJason M. Bills } 23916428a1aSJason M. Bills return true; 24016428a1aSJason M. Bills } 24116428a1aSJason M. Bills 24216428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID, 24316428a1aSJason M. Bills uint64_t ×tamp, uint16_t &index) 24416428a1aSJason M. Bills { 24516428a1aSJason M. Bills if (entryID.empty()) 24616428a1aSJason M. Bills { 24716428a1aSJason M. Bills return false; 24816428a1aSJason M. Bills } 24916428a1aSJason M. Bills // Convert the unique ID back to a timestamp to find the entry 25039e77504SEd Tanous std::string_view tsStr(entryID); 25116428a1aSJason M. Bills 25216428a1aSJason M. Bills auto underscorePos = tsStr.find("_"); 25316428a1aSJason M. Bills if (underscorePos != tsStr.npos) 25416428a1aSJason M. Bills { 25516428a1aSJason M. Bills // Timestamp has an index 25616428a1aSJason M. Bills tsStr.remove_suffix(tsStr.size() - underscorePos); 25739e77504SEd Tanous std::string_view indexStr(entryID); 25816428a1aSJason M. Bills indexStr.remove_prefix(underscorePos + 1); 25916428a1aSJason M. Bills std::size_t pos; 26016428a1aSJason M. Bills try 26116428a1aSJason M. Bills { 26239e77504SEd Tanous index = std::stoul(std::string(indexStr), &pos); 26316428a1aSJason M. Bills } 264b01bf299SEd Tanous catch (std::invalid_argument) 26516428a1aSJason M. Bills { 26616428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 26716428a1aSJason M. Bills return false; 26816428a1aSJason M. Bills } 269b01bf299SEd Tanous catch (std::out_of_range) 27016428a1aSJason M. Bills { 27116428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 27216428a1aSJason M. Bills return false; 27316428a1aSJason M. Bills } 27416428a1aSJason M. Bills if (pos != indexStr.size()) 27516428a1aSJason M. Bills { 27616428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 27716428a1aSJason M. Bills return false; 27816428a1aSJason M. Bills } 27916428a1aSJason M. Bills } 28016428a1aSJason M. Bills // Timestamp has no index 28116428a1aSJason M. Bills std::size_t pos; 28216428a1aSJason M. Bills try 28316428a1aSJason M. Bills { 28439e77504SEd Tanous timestamp = std::stoull(std::string(tsStr), &pos); 28516428a1aSJason M. Bills } 286b01bf299SEd Tanous catch (std::invalid_argument) 28716428a1aSJason M. Bills { 28816428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 28916428a1aSJason M. Bills return false; 29016428a1aSJason M. Bills } 291b01bf299SEd Tanous catch (std::out_of_range) 29216428a1aSJason M. Bills { 29316428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 29416428a1aSJason M. Bills return false; 29516428a1aSJason M. Bills } 29616428a1aSJason M. Bills if (pos != tsStr.size()) 29716428a1aSJason M. Bills { 29816428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 29916428a1aSJason M. Bills return false; 30016428a1aSJason M. Bills } 30116428a1aSJason M. Bills return true; 30216428a1aSJason M. Bills } 30316428a1aSJason M. Bills 304c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node 3051da66f75SEd Tanous { 3061da66f75SEd Tanous public: 3071da66f75SEd Tanous template <typename CrowApp> 308c4bf6374SJason M. Bills SystemLogServiceCollection(CrowApp &app) : 309029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/") 310c4bf6374SJason M. Bills { 311c4bf6374SJason M. Bills entityPrivileges = { 312c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 313c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 314c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 315c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 316c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 317c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 318c4bf6374SJason M. Bills } 319c4bf6374SJason M. Bills 320c4bf6374SJason M. Bills private: 321c4bf6374SJason M. Bills /** 322c4bf6374SJason M. Bills * Functions triggers appropriate requests on DBus 323c4bf6374SJason M. Bills */ 324c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 325c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 326c4bf6374SJason M. Bills { 327c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 328c4bf6374SJason M. Bills // Collections don't include the static data added by SubRoute because 329c4bf6374SJason M. Bills // it has a duplicate entry for members 330c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 331c4bf6374SJason M. Bills "#LogServiceCollection.LogServiceCollection"; 332c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 333c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection"; 334c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 335029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices"; 336c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "System Log Services Collection"; 337c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = 338c4bf6374SJason M. Bills "Collection of LogServices for this Computer System"; 339c4bf6374SJason M. Bills nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; 340c4bf6374SJason M. Bills logServiceArray = nlohmann::json::array(); 341029573d4SEd Tanous logServiceArray.push_back( 342029573d4SEd Tanous {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}}); 343d53dd41fSJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG 344d53dd41fSJason M. Bills logServiceArray.push_back( 345*cb92c03bSAndrew Geissler {{ "@odata.id", 346*cb92c03bSAndrew Geissler "/redfish/v1/Systems/system/LogServices/CpuLog" }}); 347d53dd41fSJason M. Bills #endif 348c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 349c4bf6374SJason M. Bills logServiceArray.size(); 350c4bf6374SJason M. Bills } 351c4bf6374SJason M. Bills }; 352c4bf6374SJason M. Bills 353c4bf6374SJason M. Bills class EventLogService : public Node 354c4bf6374SJason M. Bills { 355c4bf6374SJason M. Bills public: 356c4bf6374SJason M. Bills template <typename CrowApp> 357c4bf6374SJason M. Bills EventLogService(CrowApp &app) : 358029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/") 359c4bf6374SJason M. Bills { 360c4bf6374SJason M. Bills entityPrivileges = { 361c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 362c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 363c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 364c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 365c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 366c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 367c4bf6374SJason M. Bills } 368c4bf6374SJason M. Bills 369c4bf6374SJason M. Bills private: 370c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 371c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 372c4bf6374SJason M. Bills { 373c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 374c4bf6374SJason M. Bills 375c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 376029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog"; 377c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 378c4bf6374SJason M. Bills "#LogService.v1_1_0.LogService"; 379c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 380c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 381c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "Event Log Service"; 382c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = "System Event Log Service"; 383c4bf6374SJason M. Bills asyncResp->res.jsonValue["Id"] = "Event Log"; 384c4bf6374SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 385c4bf6374SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 386c4bf6374SJason M. Bills {"@odata.id", 387029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}}; 388c4bf6374SJason M. Bills } 389c4bf6374SJason M. Bills }; 390c4bf6374SJason M. Bills 391029573d4SEd Tanous static int fillEventLogEntryJson(const std::string &bmcLogEntryID, 39239e77504SEd Tanous const std::string_view &messageID, 393c4bf6374SJason M. Bills sd_journal *journal, 394c4bf6374SJason M. Bills nlohmann::json &bmcLogEntryJson) 395c4bf6374SJason M. Bills { 396c4bf6374SJason M. Bills // Get the Log Entry contents 397c4bf6374SJason M. Bills int ret = 0; 398c4bf6374SJason M. Bills 39939e77504SEd Tanous std::string_view msg; 400c4bf6374SJason M. Bills ret = getJournalMetadata(journal, "MESSAGE", msg); 401c4bf6374SJason M. Bills if (ret < 0) 402c4bf6374SJason M. Bills { 403c4bf6374SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret); 404c4bf6374SJason M. Bills return 1; 405c4bf6374SJason M. Bills } 406c4bf6374SJason M. Bills 407c4bf6374SJason M. Bills // Get the severity from the PRIORITY field 408c4bf6374SJason M. Bills int severity = 8; // Default to an invalid priority 409c4bf6374SJason M. Bills ret = getJournalMetadata(journal, "PRIORITY", 10, severity); 410c4bf6374SJason M. Bills if (ret < 0) 411c4bf6374SJason M. Bills { 412c4bf6374SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret); 413c4bf6374SJason M. Bills return 1; 414c4bf6374SJason M. Bills } 415c4bf6374SJason M. Bills 416c4bf6374SJason M. Bills // Get the MessageArgs from the journal entry by finding all of the 417c4bf6374SJason M. Bills // REDFISH_MESSAGE_ARG_x fields 418c4bf6374SJason M. Bills const void *data; 419c4bf6374SJason M. Bills size_t length; 420c4bf6374SJason M. Bills std::vector<std::string> messageArgs; 421c4bf6374SJason M. Bills SD_JOURNAL_FOREACH_DATA(journal, data, length) 422c4bf6374SJason M. Bills { 42339e77504SEd Tanous std::string_view field(static_cast<const char *>(data), length); 42439e77504SEd Tanous if (boost::starts_with(field, "REDFISH_MESSAGE_ARG_")) 425c4bf6374SJason M. Bills { 426c4bf6374SJason M. Bills // Get the Arg number from the field name 427c4bf6374SJason M. Bills field.remove_prefix(sizeof("REDFISH_MESSAGE_ARG_") - 1); 428c4bf6374SJason M. Bills if (field.empty()) 429c4bf6374SJason M. Bills { 430c4bf6374SJason M. Bills continue; 431c4bf6374SJason M. Bills } 432b01bf299SEd Tanous int argNum = std::strtoul(field.data(), nullptr, 10); 433b01bf299SEd Tanous if (argNum == 0) 434c4bf6374SJason M. Bills { 435c4bf6374SJason M. Bills continue; 436c4bf6374SJason M. Bills } 437c4bf6374SJason M. Bills // Get the Arg value after the "=" character. 438c4bf6374SJason M. Bills field.remove_prefix(std::min(field.find("=") + 1, field.size())); 439c4bf6374SJason M. Bills // Make sure we have enough space in messageArgs 440c4bf6374SJason M. Bills if (argNum > messageArgs.size()) 441c4bf6374SJason M. Bills { 442b01bf299SEd Tanous messageArgs.resize(argNum); 443c4bf6374SJason M. Bills } 44439e77504SEd Tanous messageArgs[argNum - 1] = std::string(field); 445c4bf6374SJason M. Bills } 446c4bf6374SJason M. Bills } 447c4bf6374SJason M. Bills 448c4bf6374SJason M. Bills // Get the Created time from the timestamp 449c4bf6374SJason M. Bills std::string entryTimeStr; 450c4bf6374SJason M. Bills if (!getEntryTimestamp(journal, entryTimeStr)) 451c4bf6374SJason M. Bills { 452c4bf6374SJason M. Bills return 1; 453c4bf6374SJason M. Bills } 454c4bf6374SJason M. Bills 455c4bf6374SJason M. Bills // Fill in the log entry with the gathered data 456c4bf6374SJason M. Bills bmcLogEntryJson = { 457*cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 458c4bf6374SJason M. Bills {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 459029573d4SEd Tanous {"@odata.id", 460029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + 461029573d4SEd Tanous bmcLogEntryID}, 462c4bf6374SJason M. Bills {"Name", "System Event Log Entry"}, 463c4bf6374SJason M. Bills {"Id", bmcLogEntryID}, 464c4bf6374SJason M. Bills {"Message", msg}, 465c4bf6374SJason M. Bills {"MessageId", messageID}, 466c4bf6374SJason M. Bills {"MessageArgs", std::move(messageArgs)}, 467c4bf6374SJason M. Bills {"EntryType", "Event"}, 468c4bf6374SJason M. Bills {"Severity", 469c4bf6374SJason M. Bills severity <= 2 ? "Critical" 470c4bf6374SJason M. Bills : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""}, 471c4bf6374SJason M. Bills {"Created", std::move(entryTimeStr)}}; 472c4bf6374SJason M. Bills return 0; 473c4bf6374SJason M. Bills } 474c4bf6374SJason M. Bills 475c4bf6374SJason M. Bills class EventLogEntryCollection : public Node 476c4bf6374SJason M. Bills { 477c4bf6374SJason M. Bills public: 478c4bf6374SJason M. Bills template <typename CrowApp> 479c4bf6374SJason M. Bills EventLogEntryCollection(CrowApp &app) : 480029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/") 481c4bf6374SJason M. Bills { 482c4bf6374SJason M. Bills entityPrivileges = { 483c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 484c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 485c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 486c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 487c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 488c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 489c4bf6374SJason M. Bills } 490c4bf6374SJason M. Bills 491c4bf6374SJason M. Bills private: 492c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 493c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 494c4bf6374SJason M. Bills { 495c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 496c4bf6374SJason M. Bills long skip = 0; 497c4bf6374SJason M. Bills long top = maxEntriesPerPage; // Show max entries by default 498c4bf6374SJason M. Bills if (!getSkipParam(asyncResp->res, req, skip)) 499c4bf6374SJason M. Bills { 500c4bf6374SJason M. Bills return; 501c4bf6374SJason M. Bills } 502c4bf6374SJason M. Bills if (!getTopParam(asyncResp->res, req, top)) 503c4bf6374SJason M. Bills { 504c4bf6374SJason M. Bills return; 505c4bf6374SJason M. Bills } 506c4bf6374SJason M. Bills // Collections don't include the static data added by SubRoute because 507c4bf6374SJason M. Bills // it has a duplicate entry for members 508c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 509c4bf6374SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 510c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 511c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection"; 512c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 513029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries"; 514c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 515c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = 516c4bf6374SJason M. Bills "Collection of System Event Log Entries"; 517*cb92c03bSAndrew Geissler 518*cb92c03bSAndrew Geissler #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES 519c4bf6374SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 520c4bf6374SJason M. Bills logEntryArray = nlohmann::json::array(); 521c4bf6374SJason M. Bills // Go through the journal and create a unique ID for each entry 522c4bf6374SJason M. Bills sd_journal *journalTmp = nullptr; 523c4bf6374SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 524c4bf6374SJason M. Bills if (ret < 0) 525c4bf6374SJason M. Bills { 526c4bf6374SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 527c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 528c4bf6374SJason M. Bills return; 529c4bf6374SJason M. Bills } 530c4bf6374SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 531c4bf6374SJason M. Bills journalTmp, sd_journal_close); 532c4bf6374SJason M. Bills journalTmp = nullptr; 533b01bf299SEd Tanous uint64_t entryCount = 0; 534c4bf6374SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 535c4bf6374SJason M. Bills { 536c4bf6374SJason M. Bills // Look for only journal entries that contain a REDFISH_MESSAGE_ID 537c4bf6374SJason M. Bills // field 53839e77504SEd Tanous std::string_view messageID; 539c4bf6374SJason M. Bills ret = getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", 540c4bf6374SJason M. Bills messageID); 541c4bf6374SJason M. Bills if (ret < 0) 542c4bf6374SJason M. Bills { 543c4bf6374SJason M. Bills continue; 544c4bf6374SJason M. Bills } 545c4bf6374SJason M. Bills 546c4bf6374SJason M. Bills entryCount++; 547c4bf6374SJason M. Bills // Handle paging using skip (number of entries to skip from the 548c4bf6374SJason M. Bills // start) and top (number of entries to display) 549c4bf6374SJason M. Bills if (entryCount <= skip || entryCount > skip + top) 550c4bf6374SJason M. Bills { 551c4bf6374SJason M. Bills continue; 552c4bf6374SJason M. Bills } 553c4bf6374SJason M. Bills 554c4bf6374SJason M. Bills std::string idStr; 555c4bf6374SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr)) 556c4bf6374SJason M. Bills { 557c4bf6374SJason M. Bills continue; 558c4bf6374SJason M. Bills } 559c4bf6374SJason M. Bills 560c4bf6374SJason M. Bills logEntryArray.push_back({}); 561c4bf6374SJason M. Bills nlohmann::json &bmcLogEntry = logEntryArray.back(); 562029573d4SEd Tanous if (fillEventLogEntryJson(idStr, messageID, journal.get(), 563c4bf6374SJason M. Bills bmcLogEntry) != 0) 564c4bf6374SJason M. Bills { 565c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 566c4bf6374SJason M. Bills return; 567c4bf6374SJason M. Bills } 568c4bf6374SJason M. Bills } 569c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 570c4bf6374SJason M. Bills if (skip + top < entryCount) 571c4bf6374SJason M. Bills { 572c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 573c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" + 574c4bf6374SJason M. Bills std::to_string(skip + top); 575c4bf6374SJason M. Bills } 576*cb92c03bSAndrew Geissler #else 577*cb92c03bSAndrew Geissler // DBus implementation of EventLog/Entries 578*cb92c03bSAndrew Geissler // Make call to Logging Service to find all log entry objects 579*cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 580*cb92c03bSAndrew Geissler [asyncResp](const boost::system::error_code ec, 581*cb92c03bSAndrew Geissler GetManagedObjectsType &resp) { 582*cb92c03bSAndrew Geissler if (ec) 583*cb92c03bSAndrew Geissler { 584*cb92c03bSAndrew Geissler // TODO Handle for specific error code 585*cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR 586*cb92c03bSAndrew Geissler << "getLogEntriesIfaceData resp_handler got error " 587*cb92c03bSAndrew Geissler << ec; 588*cb92c03bSAndrew Geissler messages::internalError(asyncResp->res); 589*cb92c03bSAndrew Geissler return; 590*cb92c03bSAndrew Geissler } 591*cb92c03bSAndrew Geissler nlohmann::json &entriesArray = 592*cb92c03bSAndrew Geissler asyncResp->res.jsonValue["Members"]; 593*cb92c03bSAndrew Geissler entriesArray = nlohmann::json::array(); 594*cb92c03bSAndrew Geissler for (auto &objectPath : resp) 595*cb92c03bSAndrew Geissler { 596*cb92c03bSAndrew Geissler for (auto &interfaceMap : objectPath.second) 597*cb92c03bSAndrew Geissler { 598*cb92c03bSAndrew Geissler if (interfaceMap.first != 599*cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging.Entry") 600*cb92c03bSAndrew Geissler { 601*cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "Bailing early on " 602*cb92c03bSAndrew Geissler << interfaceMap.first; 603*cb92c03bSAndrew Geissler continue; 604*cb92c03bSAndrew Geissler } 605*cb92c03bSAndrew Geissler entriesArray.push_back({}); 606*cb92c03bSAndrew Geissler nlohmann::json &thisEntry = entriesArray.back(); 607*cb92c03bSAndrew Geissler uint32_t *id; 608*cb92c03bSAndrew Geissler std::time_t timestamp; 609*cb92c03bSAndrew Geissler std::string *severity, *message; 610*cb92c03bSAndrew Geissler bool *resolved; 611*cb92c03bSAndrew Geissler for (auto &propertyMap : interfaceMap.second) 612*cb92c03bSAndrew Geissler { 613*cb92c03bSAndrew Geissler if (propertyMap.first == "Id") 614*cb92c03bSAndrew Geissler { 615*cb92c03bSAndrew Geissler id = sdbusplus::message::variant_ns::get_if< 616*cb92c03bSAndrew Geissler uint32_t>(&propertyMap.second); 617*cb92c03bSAndrew Geissler if (id == nullptr) 618*cb92c03bSAndrew Geissler { 619*cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 620*cb92c03bSAndrew Geissler "Id"); 621*cb92c03bSAndrew Geissler } 622*cb92c03bSAndrew Geissler } 623*cb92c03bSAndrew Geissler else if (propertyMap.first == "Timestamp") 624*cb92c03bSAndrew Geissler { 625*cb92c03bSAndrew Geissler const uint64_t *millisTimeStamp = 626*cb92c03bSAndrew Geissler std::get_if<uint64_t>(&propertyMap.second); 627*cb92c03bSAndrew Geissler if (millisTimeStamp == nullptr) 628*cb92c03bSAndrew Geissler { 629*cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 630*cb92c03bSAndrew Geissler "Timestamp"); 631*cb92c03bSAndrew Geissler } 632*cb92c03bSAndrew Geissler // Retrieve Created property with format: 633*cb92c03bSAndrew Geissler // yyyy-mm-ddThh:mm:ss 634*cb92c03bSAndrew Geissler std::chrono::milliseconds chronoTimeStamp( 635*cb92c03bSAndrew Geissler *millisTimeStamp); 636*cb92c03bSAndrew Geissler timestamp = 637*cb92c03bSAndrew Geissler std::chrono::duration_cast< 638*cb92c03bSAndrew Geissler std::chrono::seconds>(chronoTimeStamp) 639*cb92c03bSAndrew Geissler .count(); 640*cb92c03bSAndrew Geissler } 641*cb92c03bSAndrew Geissler else if (propertyMap.first == "Severity") 642*cb92c03bSAndrew Geissler { 643*cb92c03bSAndrew Geissler severity = std::get_if<std::string>( 644*cb92c03bSAndrew Geissler &propertyMap.second); 645*cb92c03bSAndrew Geissler if (severity == nullptr) 646*cb92c03bSAndrew Geissler { 647*cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 648*cb92c03bSAndrew Geissler "Severity"); 649*cb92c03bSAndrew Geissler } 650*cb92c03bSAndrew Geissler } 651*cb92c03bSAndrew Geissler else if (propertyMap.first == "Message") 652*cb92c03bSAndrew Geissler { 653*cb92c03bSAndrew Geissler message = std::get_if<std::string>( 654*cb92c03bSAndrew Geissler &propertyMap.second); 655*cb92c03bSAndrew Geissler if (message == nullptr) 656*cb92c03bSAndrew Geissler { 657*cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 658*cb92c03bSAndrew Geissler "Message"); 659*cb92c03bSAndrew Geissler } 660*cb92c03bSAndrew Geissler } 661*cb92c03bSAndrew Geissler } 662*cb92c03bSAndrew Geissler thisEntry = { 663*cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 664*cb92c03bSAndrew Geissler {"@odata.context", "/redfish/v1/" 665*cb92c03bSAndrew Geissler "$metadata#LogEntry.LogEntry"}, 666*cb92c03bSAndrew Geissler {"@odata.id", 667*cb92c03bSAndrew Geissler "/redfish/v1/Systems/system/LogServices/EventLog/" 668*cb92c03bSAndrew Geissler "Entries/" + 669*cb92c03bSAndrew Geissler std::to_string(*id)}, 670*cb92c03bSAndrew Geissler {"Name", "System DBus Event Log Entry"}, 671*cb92c03bSAndrew Geissler {"Id", std::to_string(*id)}, 672*cb92c03bSAndrew Geissler {"Message", *message}, 673*cb92c03bSAndrew Geissler {"EntryType", "Event"}, 674*cb92c03bSAndrew Geissler {"Severity", 675*cb92c03bSAndrew Geissler translateSeverityDbusToRedfish(*severity)}, 676*cb92c03bSAndrew Geissler {"Created", crow::utility::getDateTime(timestamp)}}; 677*cb92c03bSAndrew Geissler } 678*cb92c03bSAndrew Geissler } 679*cb92c03bSAndrew Geissler std::sort(entriesArray.begin(), entriesArray.end(), 680*cb92c03bSAndrew Geissler [](const nlohmann::json &left, 681*cb92c03bSAndrew Geissler const nlohmann::json &right) { 682*cb92c03bSAndrew Geissler return (left["Id"] <= right["Id"]); 683*cb92c03bSAndrew Geissler }); 684*cb92c03bSAndrew Geissler asyncResp->res.jsonValue["Members@odata.count"] = 685*cb92c03bSAndrew Geissler entriesArray.size(); 686*cb92c03bSAndrew Geissler }, 687*cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging", 688*cb92c03bSAndrew Geissler "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 689*cb92c03bSAndrew Geissler #endif 690c4bf6374SJason M. Bills } 691c4bf6374SJason M. Bills }; 692c4bf6374SJason M. Bills 693c4bf6374SJason M. Bills class EventLogEntry : public Node 694c4bf6374SJason M. Bills { 695c4bf6374SJason M. Bills public: 696c4bf6374SJason M. Bills EventLogEntry(CrowApp &app) : 697c4bf6374SJason M. Bills Node(app, 698029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/", 699029573d4SEd Tanous std::string()) 700c4bf6374SJason M. Bills { 701c4bf6374SJason M. Bills entityPrivileges = { 702c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 703c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 704c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 705c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 706c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 707c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 708c4bf6374SJason M. Bills } 709c4bf6374SJason M. Bills 710c4bf6374SJason M. Bills private: 711c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 712c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 713c4bf6374SJason M. Bills { 714c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 715029573d4SEd Tanous if (params.size() != 1) 716c4bf6374SJason M. Bills { 717c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 718c4bf6374SJason M. Bills return; 719c4bf6374SJason M. Bills } 720029573d4SEd Tanous const std::string &entryID = params[0]; 721*cb92c03bSAndrew Geissler 722*cb92c03bSAndrew Geissler #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES 723c4bf6374SJason M. Bills // Convert the unique ID back to a timestamp to find the entry 724c4bf6374SJason M. Bills uint64_t ts = 0; 725c4bf6374SJason M. Bills uint16_t index = 0; 726c4bf6374SJason M. Bills if (!getTimestampFromID(asyncResp->res, entryID, ts, index)) 727c4bf6374SJason M. Bills { 728c4bf6374SJason M. Bills return; 729c4bf6374SJason M. Bills } 730c4bf6374SJason M. Bills 731c4bf6374SJason M. Bills sd_journal *journalTmp = nullptr; 732c4bf6374SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 733c4bf6374SJason M. Bills if (ret < 0) 734c4bf6374SJason M. Bills { 735c4bf6374SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 736c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 737c4bf6374SJason M. Bills return; 738c4bf6374SJason M. Bills } 739c4bf6374SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 740c4bf6374SJason M. Bills journalTmp, sd_journal_close); 741c4bf6374SJason M. Bills journalTmp = nullptr; 742c4bf6374SJason M. Bills // Go to the timestamp in the log and move to the entry at the index 743c4bf6374SJason M. Bills ret = sd_journal_seek_realtime_usec(journal.get(), ts); 744c4bf6374SJason M. Bills for (int i = 0; i <= index; i++) 745c4bf6374SJason M. Bills { 746c4bf6374SJason M. Bills sd_journal_next(journal.get()); 747c4bf6374SJason M. Bills } 748c4bf6374SJason M. Bills // Confirm that the entry ID matches what was requested 749c4bf6374SJason M. Bills std::string idStr; 750c4bf6374SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID) 751c4bf6374SJason M. Bills { 752c4bf6374SJason M. Bills messages::resourceMissingAtURI(asyncResp->res, entryID); 753c4bf6374SJason M. Bills return; 754c4bf6374SJason M. Bills } 755c4bf6374SJason M. Bills 756cd50aa42SJason M. Bills // only use journal entries that contain a REDFISH_MESSAGE_ID field 75739e77504SEd Tanous std::string_view messageID; 758c4bf6374SJason M. Bills ret = 759c4bf6374SJason M. Bills getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", messageID); 760c4bf6374SJason M. Bills if (ret < 0) 761c4bf6374SJason M. Bills { 762029573d4SEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", "system"); 763c4bf6374SJason M. Bills return; 764c4bf6374SJason M. Bills } 765c4bf6374SJason M. Bills 766029573d4SEd Tanous if (fillEventLogEntryJson(entryID, messageID, journal.get(), 767c4bf6374SJason M. Bills asyncResp->res.jsonValue) != 0) 768c4bf6374SJason M. Bills { 769c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 770c4bf6374SJason M. Bills return; 771c4bf6374SJason M. Bills } 772*cb92c03bSAndrew Geissler #else 773*cb92c03bSAndrew Geissler // DBus implementation of EventLog/Entries 774*cb92c03bSAndrew Geissler // Make call to Logging Service to find all log entry objects 775*cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 776*cb92c03bSAndrew Geissler [asyncResp, entryID](const boost::system::error_code ec, 777*cb92c03bSAndrew Geissler GetManagedPropertyType &resp) { 778*cb92c03bSAndrew Geissler if (ec) 779*cb92c03bSAndrew Geissler { 780*cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR 781*cb92c03bSAndrew Geissler << "EventLogEntry (DBus) resp_handler got error " << ec; 782*cb92c03bSAndrew Geissler messages::internalError(asyncResp->res); 783*cb92c03bSAndrew Geissler return; 784*cb92c03bSAndrew Geissler } 785*cb92c03bSAndrew Geissler uint32_t *id; 786*cb92c03bSAndrew Geissler std::time_t timestamp; 787*cb92c03bSAndrew Geissler std::string *severity, *message; 788*cb92c03bSAndrew Geissler bool *resolved; 789*cb92c03bSAndrew Geissler for (auto &propertyMap : resp) 790*cb92c03bSAndrew Geissler { 791*cb92c03bSAndrew Geissler if (propertyMap.first == "Id") 792*cb92c03bSAndrew Geissler { 793*cb92c03bSAndrew Geissler id = std::get_if<uint32_t>(&propertyMap.second); 794*cb92c03bSAndrew Geissler if (id == nullptr) 795*cb92c03bSAndrew Geissler { 796*cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, "Id"); 797*cb92c03bSAndrew Geissler } 798*cb92c03bSAndrew Geissler } 799*cb92c03bSAndrew Geissler else if (propertyMap.first == "Timestamp") 800*cb92c03bSAndrew Geissler { 801*cb92c03bSAndrew Geissler const uint64_t *millisTimeStamp = 802*cb92c03bSAndrew Geissler std::get_if<uint64_t>(&propertyMap.second); 803*cb92c03bSAndrew Geissler if (millisTimeStamp == nullptr) 804*cb92c03bSAndrew Geissler { 805*cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 806*cb92c03bSAndrew Geissler "Timestamp"); 807*cb92c03bSAndrew Geissler } 808*cb92c03bSAndrew Geissler // Retrieve Created property with format: 809*cb92c03bSAndrew Geissler // yyyy-mm-ddThh:mm:ss 810*cb92c03bSAndrew Geissler std::chrono::milliseconds chronoTimeStamp( 811*cb92c03bSAndrew Geissler *millisTimeStamp); 812*cb92c03bSAndrew Geissler timestamp = 813*cb92c03bSAndrew Geissler std::chrono::duration_cast<std::chrono::seconds>( 814*cb92c03bSAndrew Geissler chronoTimeStamp) 815*cb92c03bSAndrew Geissler .count(); 816*cb92c03bSAndrew Geissler } 817*cb92c03bSAndrew Geissler else if (propertyMap.first == "Severity") 818*cb92c03bSAndrew Geissler { 819*cb92c03bSAndrew Geissler severity = 820*cb92c03bSAndrew Geissler std::get_if<std::string>(&propertyMap.second); 821*cb92c03bSAndrew Geissler if (severity == nullptr) 822*cb92c03bSAndrew Geissler { 823*cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 824*cb92c03bSAndrew Geissler "Severity"); 825*cb92c03bSAndrew Geissler } 826*cb92c03bSAndrew Geissler } 827*cb92c03bSAndrew Geissler else if (propertyMap.first == "Message") 828*cb92c03bSAndrew Geissler { 829*cb92c03bSAndrew Geissler message = std::get_if<std::string>(&propertyMap.second); 830*cb92c03bSAndrew Geissler if (message == nullptr) 831*cb92c03bSAndrew Geissler { 832*cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 833*cb92c03bSAndrew Geissler "Message"); 834*cb92c03bSAndrew Geissler } 835*cb92c03bSAndrew Geissler } 836*cb92c03bSAndrew Geissler } 837*cb92c03bSAndrew Geissler asyncResp->res.jsonValue = { 838*cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 839*cb92c03bSAndrew Geissler {"@odata.context", "/redfish/v1/" 840*cb92c03bSAndrew Geissler "$metadata#LogEntry.LogEntry"}, 841*cb92c03bSAndrew Geissler {"@odata.id", 842*cb92c03bSAndrew Geissler "/redfish/v1/Systems/system/LogServices/EventLog/" 843*cb92c03bSAndrew Geissler "Entries/" + 844*cb92c03bSAndrew Geissler std::to_string(*id)}, 845*cb92c03bSAndrew Geissler {"Name", "System DBus Event Log Entry"}, 846*cb92c03bSAndrew Geissler {"Id", std::to_string(*id)}, 847*cb92c03bSAndrew Geissler {"Message", *message}, 848*cb92c03bSAndrew Geissler {"EntryType", "Event"}, 849*cb92c03bSAndrew Geissler {"Severity", translateSeverityDbusToRedfish(*severity)}, 850*cb92c03bSAndrew Geissler {"Created", crow::utility::getDateTime(timestamp)}}; 851*cb92c03bSAndrew Geissler }, 852*cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging", 853*cb92c03bSAndrew Geissler "/xyz/openbmc_project/logging/entry/" + entryID, 854*cb92c03bSAndrew Geissler "org.freedesktop.DBus.Properties", "GetAll", 855*cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging.Entry"); 856*cb92c03bSAndrew Geissler #endif 857c4bf6374SJason M. Bills } 858c4bf6374SJason M. Bills }; 859c4bf6374SJason M. Bills 860c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node 861c4bf6374SJason M. Bills { 862c4bf6374SJason M. Bills public: 863c4bf6374SJason M. Bills template <typename CrowApp> 864c4bf6374SJason M. Bills BMCLogServiceCollection(CrowApp &app) : 8654ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/") 8661da66f75SEd Tanous { 8671da66f75SEd Tanous entityPrivileges = { 868e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 869e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 870e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 871e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 872e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 873e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 8741da66f75SEd Tanous } 8751da66f75SEd Tanous 8761da66f75SEd Tanous private: 8771da66f75SEd Tanous /** 8781da66f75SEd Tanous * Functions triggers appropriate requests on DBus 8791da66f75SEd Tanous */ 8801da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 8811da66f75SEd Tanous const std::vector<std::string> ¶ms) override 8821da66f75SEd Tanous { 883e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 8841da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 8851da66f75SEd Tanous // it has a duplicate entry for members 886e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 8871da66f75SEd Tanous "#LogServiceCollection.LogServiceCollection"; 888e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 889c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection"; 890e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 891e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices"; 892e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection"; 893e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 8941da66f75SEd Tanous "Collection of LogServices for this Manager"; 895c4bf6374SJason M. Bills nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; 896c4bf6374SJason M. Bills logServiceArray = nlohmann::json::array(); 897c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL 898c4bf6374SJason M. Bills logServiceArray.push_back( 899*cb92c03bSAndrew Geissler {{ "@odata.id", 900*cb92c03bSAndrew Geissler "/redfish/v1/Managers/bmc/LogServices/Journal" }}); 901c4bf6374SJason M. Bills #endif 902e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 903c4bf6374SJason M. Bills logServiceArray.size(); 9041da66f75SEd Tanous } 9051da66f75SEd Tanous }; 9061da66f75SEd Tanous 907c4bf6374SJason M. Bills class BMCJournalLogService : public Node 9081da66f75SEd Tanous { 9091da66f75SEd Tanous public: 9101da66f75SEd Tanous template <typename CrowApp> 911c4bf6374SJason M. Bills BMCJournalLogService(CrowApp &app) : 912c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/") 913e1f26343SJason M. Bills { 914e1f26343SJason M. Bills entityPrivileges = { 915e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 916e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 917e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 918e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 919e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 920e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 921e1f26343SJason M. Bills } 922e1f26343SJason M. Bills 923e1f26343SJason M. Bills private: 924e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 925e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 926e1f26343SJason M. Bills { 927e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 928e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 929e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 9300f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 9310f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal"; 932e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 933e1f26343SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 934c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service"; 935c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service"; 936c4bf6374SJason M. Bills asyncResp->res.jsonValue["Id"] = "BMC Journal"; 937e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 938cd50aa42SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 939cd50aa42SJason M. Bills {"@odata.id", 940cd50aa42SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/"}}; 941e1f26343SJason M. Bills } 942e1f26343SJason M. Bills }; 943e1f26343SJason M. Bills 944c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID, 945e1f26343SJason M. Bills sd_journal *journal, 946c4bf6374SJason M. Bills nlohmann::json &bmcJournalLogEntryJson) 947e1f26343SJason M. Bills { 948e1f26343SJason M. Bills // Get the Log Entry contents 949e1f26343SJason M. Bills int ret = 0; 950e1f26343SJason M. Bills 95139e77504SEd Tanous std::string_view msg; 95216428a1aSJason M. Bills ret = getJournalMetadata(journal, "MESSAGE", msg); 953e1f26343SJason M. Bills if (ret < 0) 954e1f26343SJason M. Bills { 955e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret); 956e1f26343SJason M. Bills return 1; 957e1f26343SJason M. Bills } 958e1f26343SJason M. Bills 959e1f26343SJason M. Bills // Get the severity from the PRIORITY field 960e1f26343SJason M. Bills int severity = 8; // Default to an invalid priority 96116428a1aSJason M. Bills ret = getJournalMetadata(journal, "PRIORITY", 10, severity); 962e1f26343SJason M. Bills if (ret < 0) 963e1f26343SJason M. Bills { 964e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret); 965e1f26343SJason M. Bills return 1; 966e1f26343SJason M. Bills } 967e1f26343SJason M. Bills 968e1f26343SJason M. Bills // Get the Created time from the timestamp 96916428a1aSJason M. Bills std::string entryTimeStr; 97016428a1aSJason M. Bills if (!getEntryTimestamp(journal, entryTimeStr)) 971e1f26343SJason M. Bills { 97216428a1aSJason M. Bills return 1; 973e1f26343SJason M. Bills } 974e1f26343SJason M. Bills 975e1f26343SJason M. Bills // Fill in the log entry with the gathered data 976c4bf6374SJason M. Bills bmcJournalLogEntryJson = { 977*cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 978e1f26343SJason M. Bills {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 979c4bf6374SJason M. Bills {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" + 980c4bf6374SJason M. Bills bmcJournalLogEntryID}, 981e1f26343SJason M. Bills {"Name", "BMC Journal Entry"}, 982c4bf6374SJason M. Bills {"Id", bmcJournalLogEntryID}, 98316428a1aSJason M. Bills {"Message", msg}, 984e1f26343SJason M. Bills {"EntryType", "Oem"}, 985e1f26343SJason M. Bills {"Severity", 986e1f26343SJason M. Bills severity <= 2 ? "Critical" 987e1f26343SJason M. Bills : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""}, 988e1f26343SJason M. Bills {"OemRecordFormat", "Intel BMC Journal Entry"}, 989e1f26343SJason M. Bills {"Created", std::move(entryTimeStr)}}; 990e1f26343SJason M. Bills return 0; 991e1f26343SJason M. Bills } 992e1f26343SJason M. Bills 993c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node 994e1f26343SJason M. Bills { 995e1f26343SJason M. Bills public: 996e1f26343SJason M. Bills template <typename CrowApp> 997c4bf6374SJason M. Bills BMCJournalLogEntryCollection(CrowApp &app) : 998c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/") 999e1f26343SJason M. Bills { 1000e1f26343SJason M. Bills entityPrivileges = { 1001e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1002e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1003e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1004e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1005e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1006e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1007e1f26343SJason M. Bills } 1008e1f26343SJason M. Bills 1009e1f26343SJason M. Bills private: 1010e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1011e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1012e1f26343SJason M. Bills { 1013e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1014193ad2faSJason M. Bills static constexpr const long maxEntriesPerPage = 1000; 1015193ad2faSJason M. Bills long skip = 0; 1016193ad2faSJason M. Bills long top = maxEntriesPerPage; // Show max entries by default 101716428a1aSJason M. Bills if (!getSkipParam(asyncResp->res, req, skip)) 1018193ad2faSJason M. Bills { 1019193ad2faSJason M. Bills return; 1020193ad2faSJason M. Bills } 102116428a1aSJason M. Bills if (!getTopParam(asyncResp->res, req, top)) 1022193ad2faSJason M. Bills { 1023193ad2faSJason M. Bills return; 1024193ad2faSJason M. Bills } 1025e1f26343SJason M. Bills // Collections don't include the static data added by SubRoute because 1026e1f26343SJason M. Bills // it has a duplicate entry for members 1027e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1028e1f26343SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 10290f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 10300f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; 1031e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1032c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection"; 1033e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 1034c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; 1035e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; 1036e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 1037e1f26343SJason M. Bills "Collection of BMC Journal Entries"; 10380f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 10390f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; 1040e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 1041e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 1042e1f26343SJason M. Bills 1043e1f26343SJason M. Bills // Go through the journal and use the timestamp to create a unique ID 1044e1f26343SJason M. Bills // for each entry 1045e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 1046e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 1047e1f26343SJason M. Bills if (ret < 0) 1048e1f26343SJason M. Bills { 1049e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 1050f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1051e1f26343SJason M. Bills return; 1052e1f26343SJason M. Bills } 1053e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 1054e1f26343SJason M. Bills journalTmp, sd_journal_close); 1055e1f26343SJason M. Bills journalTmp = nullptr; 1056b01bf299SEd Tanous uint64_t entryCount = 0; 1057e1f26343SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 1058e1f26343SJason M. Bills { 1059193ad2faSJason M. Bills entryCount++; 1060193ad2faSJason M. Bills // Handle paging using skip (number of entries to skip from the 1061193ad2faSJason M. Bills // start) and top (number of entries to display) 1062193ad2faSJason M. Bills if (entryCount <= skip || entryCount > skip + top) 1063193ad2faSJason M. Bills { 1064193ad2faSJason M. Bills continue; 1065193ad2faSJason M. Bills } 1066193ad2faSJason M. Bills 106716428a1aSJason M. Bills std::string idStr; 106816428a1aSJason M. Bills if (!getUniqueEntryID(journal.get(), idStr)) 1069e1f26343SJason M. Bills { 1070e1f26343SJason M. Bills continue; 1071e1f26343SJason M. Bills } 1072e1f26343SJason M. Bills 1073e1f26343SJason M. Bills logEntryArray.push_back({}); 1074c4bf6374SJason M. Bills nlohmann::json &bmcJournalLogEntry = logEntryArray.back(); 1075c4bf6374SJason M. Bills if (fillBMCJournalLogEntryJson(idStr, journal.get(), 1076c4bf6374SJason M. Bills bmcJournalLogEntry) != 0) 1077e1f26343SJason M. Bills { 1078f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1079e1f26343SJason M. Bills return; 1080e1f26343SJason M. Bills } 1081e1f26343SJason M. Bills } 1082193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 1083193ad2faSJason M. Bills if (skip + top < entryCount) 1084193ad2faSJason M. Bills { 1085193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 1086c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" + 1087193ad2faSJason M. Bills std::to_string(skip + top); 1088193ad2faSJason M. Bills } 1089e1f26343SJason M. Bills } 1090e1f26343SJason M. Bills }; 1091e1f26343SJason M. Bills 1092c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node 1093e1f26343SJason M. Bills { 1094e1f26343SJason M. Bills public: 1095c4bf6374SJason M. Bills BMCJournalLogEntry(CrowApp &app) : 1096c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/", 1097e1f26343SJason M. Bills std::string()) 1098e1f26343SJason M. Bills { 1099e1f26343SJason M. Bills entityPrivileges = { 1100e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1101e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1102e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1103e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1104e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1105e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1106e1f26343SJason M. Bills } 1107e1f26343SJason M. Bills 1108e1f26343SJason M. Bills private: 1109e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1110e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1111e1f26343SJason M. Bills { 1112e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1113e1f26343SJason M. Bills if (params.size() != 1) 1114e1f26343SJason M. Bills { 1115f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1116e1f26343SJason M. Bills return; 1117e1f26343SJason M. Bills } 111816428a1aSJason M. Bills const std::string &entryID = params[0]; 1119e1f26343SJason M. Bills // Convert the unique ID back to a timestamp to find the entry 1120e1f26343SJason M. Bills uint64_t ts = 0; 1121e1f26343SJason M. Bills uint16_t index = 0; 112216428a1aSJason M. Bills if (!getTimestampFromID(asyncResp->res, entryID, ts, index)) 1123e1f26343SJason M. Bills { 112416428a1aSJason M. Bills return; 1125e1f26343SJason M. Bills } 1126e1f26343SJason M. Bills 1127e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 1128e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 1129e1f26343SJason M. Bills if (ret < 0) 1130e1f26343SJason M. Bills { 1131e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 1132f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1133e1f26343SJason M. Bills return; 1134e1f26343SJason M. Bills } 1135e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 1136e1f26343SJason M. Bills journalTmp, sd_journal_close); 1137e1f26343SJason M. Bills journalTmp = nullptr; 1138e1f26343SJason M. Bills // Go to the timestamp in the log and move to the entry at the index 1139e1f26343SJason M. Bills ret = sd_journal_seek_realtime_usec(journal.get(), ts); 1140e1f26343SJason M. Bills for (int i = 0; i <= index; i++) 1141e1f26343SJason M. Bills { 1142e1f26343SJason M. Bills sd_journal_next(journal.get()); 1143e1f26343SJason M. Bills } 1144c4bf6374SJason M. Bills // Confirm that the entry ID matches what was requested 1145c4bf6374SJason M. Bills std::string idStr; 1146c4bf6374SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID) 1147c4bf6374SJason M. Bills { 1148c4bf6374SJason M. Bills messages::resourceMissingAtURI(asyncResp->res, entryID); 1149c4bf6374SJason M. Bills return; 1150c4bf6374SJason M. Bills } 1151c4bf6374SJason M. Bills 1152c4bf6374SJason M. Bills if (fillBMCJournalLogEntryJson(entryID, journal.get(), 1153e1f26343SJason M. Bills asyncResp->res.jsonValue) != 0) 1154e1f26343SJason M. Bills { 1155f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1156e1f26343SJason M. Bills return; 1157e1f26343SJason M. Bills } 1158e1f26343SJason M. Bills } 1159e1f26343SJason M. Bills }; 1160e1f26343SJason M. Bills 1161e1f26343SJason M. Bills class CPULogService : public Node 1162e1f26343SJason M. Bills { 1163e1f26343SJason M. Bills public: 1164e1f26343SJason M. Bills template <typename CrowApp> 1165e1f26343SJason M. Bills CPULogService(CrowApp &app) : 1166d53dd41fSJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/CpuLog/") 11671da66f75SEd Tanous { 11681da66f75SEd Tanous entityPrivileges = { 1169e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1170e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1171e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1172e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1173e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1174e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 11751da66f75SEd Tanous } 11761da66f75SEd Tanous 11771da66f75SEd Tanous private: 11781da66f75SEd Tanous /** 11791da66f75SEd Tanous * Functions triggers appropriate requests on DBus 11801da66f75SEd Tanous */ 11811da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 11821da66f75SEd Tanous const std::vector<std::string> ¶ms) override 11831da66f75SEd Tanous { 1184e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 11851da66f75SEd Tanous // Copy over the static data to include the entries added by SubRoute 11860f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 1187d53dd41fSJason M. Bills "/redfish/v1/Systems/system/LogServices/CpuLog"; 1188e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1189e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 1190e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1191c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 1192e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service"; 1193e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = "CPU Log Service"; 1194e1f26343SJason M. Bills asyncResp->res.jsonValue["Id"] = "CPU Log"; 1195e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 1196e1f26343SJason M. Bills asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 1197cd50aa42SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 1198cd50aa42SJason M. Bills {"@odata.id", 1199f9fc2dffSJason M. Bills "/redfish/v1/Systems/system/LogServices/CpuLog/Entries"}}; 1200e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"] = { 12011da66f75SEd Tanous {"Oem", 12021da66f75SEd Tanous {{"#CpuLog.Immediate", 1203d53dd41fSJason M. Bills {{"target", "/redfish/v1/Systems/system/LogServices/CpuLog/" 1204c4bf6374SJason M. Bills "Actions/Oem/CpuLog.Immediate"}}}}}}; 12051da66f75SEd Tanous 12061da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI 1207e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"]["Oem"].push_back( 12081da66f75SEd Tanous {"#CpuLog.SendRawPeci", 1209*cb92c03bSAndrew Geissler { { "target", 1210*cb92c03bSAndrew Geissler "/redfish/v1/Systems/system/LogServices/CpuLog/" 1211d53dd41fSJason M. Bills "Actions/Oem/CpuLog.SendRawPeci" } }}); 12121da66f75SEd Tanous #endif 12131da66f75SEd Tanous } 12141da66f75SEd Tanous }; 12151da66f75SEd Tanous 1216e1f26343SJason M. Bills class CPULogEntryCollection : public Node 12171da66f75SEd Tanous { 12181da66f75SEd Tanous public: 12191da66f75SEd Tanous template <typename CrowApp> 1220e1f26343SJason M. Bills CPULogEntryCollection(CrowApp &app) : 1221d53dd41fSJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/CpuLog/Entries/") 12221da66f75SEd Tanous { 12231da66f75SEd Tanous entityPrivileges = { 1224e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1225e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1226e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1227e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1228e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1229e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 12301da66f75SEd Tanous } 12311da66f75SEd Tanous 12321da66f75SEd Tanous private: 12331da66f75SEd Tanous /** 12341da66f75SEd Tanous * Functions triggers appropriate requests on DBus 12351da66f75SEd Tanous */ 12361da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 12371da66f75SEd Tanous const std::vector<std::string> ¶ms) override 12381da66f75SEd Tanous { 1239e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 12401da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 12411da66f75SEd Tanous // it has a duplicate entry for members 1242e1f26343SJason M. Bills auto getLogEntriesCallback = [asyncResp]( 1243e1f26343SJason M. Bills const boost::system::error_code ec, 12441da66f75SEd Tanous const std::vector<std::string> &resp) { 12451da66f75SEd Tanous if (ec) 12461da66f75SEd Tanous { 12471da66f75SEd Tanous if (ec.value() != 12481da66f75SEd Tanous boost::system::errc::no_such_file_or_directory) 12491da66f75SEd Tanous { 12501da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to get entries ec: " 12511da66f75SEd Tanous << ec.message(); 1252f12894f8SJason M. Bills messages::internalError(asyncResp->res); 12531da66f75SEd Tanous return; 12541da66f75SEd Tanous } 12551da66f75SEd Tanous } 1256e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 12571da66f75SEd Tanous "#LogEntryCollection.LogEntryCollection"; 12580f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 1259d53dd41fSJason M. Bills "/redfish/v1/Systems/system/LogServices/CpuLog/Entries"; 1260e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1261d53dd41fSJason M. Bills "/redfish/v1/" 1262d53dd41fSJason M. Bills "$metadata#LogEntryCollection.LogEntryCollection"; 1263e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries"; 1264e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 1265e1f26343SJason M. Bills "Collection of CPU Log Entries"; 1266e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 1267e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 12681da66f75SEd Tanous for (const std::string &objpath : resp) 12691da66f75SEd Tanous { 12701da66f75SEd Tanous // Don't list the immediate log 12714ed77cd5SEd Tanous if (objpath.compare(cpuLogImmediatePath) == 0) 12721da66f75SEd Tanous { 12731da66f75SEd Tanous continue; 12741da66f75SEd Tanous } 12754ed77cd5SEd Tanous std::size_t lastPos = objpath.rfind("/"); 12764ed77cd5SEd Tanous if (lastPos != std::string::npos) 12771da66f75SEd Tanous { 1278e1f26343SJason M. Bills logEntryArray.push_back( 1279d53dd41fSJason M. Bills {{"@odata.id", "/redfish/v1/Systems/system/LogServices/" 1280e1f26343SJason M. Bills "CpuLog/Entries/" + 12814ed77cd5SEd Tanous objpath.substr(lastPos + 1)}}); 12821da66f75SEd Tanous } 12831da66f75SEd Tanous } 1284e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 1285e1f26343SJason M. Bills logEntryArray.size(); 12861da66f75SEd Tanous }; 12871da66f75SEd Tanous crow::connections::systemBus->async_method_call( 12881da66f75SEd Tanous std::move(getLogEntriesCallback), 12891da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", 12901da66f75SEd Tanous "/xyz/openbmc_project/object_mapper", 12911da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0, 12924ed77cd5SEd Tanous std::array<const char *, 1>{cpuLogInterface}); 12931da66f75SEd Tanous } 12941da66f75SEd Tanous }; 12951da66f75SEd Tanous 12961da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog) 12971da66f75SEd Tanous { 1298c4d00437SJason M. Bills nlohmann::json::const_iterator cdIt = cpuLog.find("crashlog_data"); 1299c4d00437SJason M. Bills if (cdIt != cpuLog.end()) 13001da66f75SEd Tanous { 1301c4d00437SJason M. Bills nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO"); 1302c4d00437SJason M. Bills if (siIt != cdIt->end()) 13031da66f75SEd Tanous { 1304c4d00437SJason M. Bills nlohmann::json::const_iterator tsIt = siIt->find("timestamp"); 1305c4d00437SJason M. Bills if (tsIt != siIt->end()) 1306c4d00437SJason M. Bills { 1307c4d00437SJason M. Bills const std::string *logTime = 1308c4d00437SJason M. Bills tsIt->get_ptr<const std::string *>(); 13091da66f75SEd Tanous if (logTime != nullptr) 13101da66f75SEd Tanous { 13111da66f75SEd Tanous return *logTime; 13121da66f75SEd Tanous } 13131da66f75SEd Tanous } 13141da66f75SEd Tanous } 1315c4d00437SJason M. Bills } 13161da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to find log timestamp"; 13171da66f75SEd Tanous 13181da66f75SEd Tanous return std::string(); 13191da66f75SEd Tanous } 13201da66f75SEd Tanous 1321e1f26343SJason M. Bills class CPULogEntry : public Node 13221da66f75SEd Tanous { 13231da66f75SEd Tanous public: 1324e1f26343SJason M. Bills CPULogEntry(CrowApp &app) : 1325d53dd41fSJason M. Bills Node(app, 1326d53dd41fSJason M. Bills "/redfish/v1/Systems/system/LogServices/CpuLog/Entries/<str>/", 13271da66f75SEd Tanous std::string()) 13281da66f75SEd Tanous { 13291da66f75SEd Tanous entityPrivileges = { 1330e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1331e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1332e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1333e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1334e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1335e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 13361da66f75SEd Tanous } 13371da66f75SEd Tanous 13381da66f75SEd Tanous private: 13391da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 13401da66f75SEd Tanous const std::vector<std::string> ¶ms) override 13411da66f75SEd Tanous { 1342e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 13431da66f75SEd Tanous if (params.size() != 1) 13441da66f75SEd Tanous { 1345f12894f8SJason M. Bills messages::internalError(asyncResp->res); 13461da66f75SEd Tanous return; 13471da66f75SEd Tanous } 1348b01bf299SEd Tanous const uint8_t logId = std::atoi(params[0].c_str()); 1349abf2add6SEd Tanous auto getStoredLogCallback = [asyncResp, logId]( 1350abf2add6SEd Tanous const boost::system::error_code ec, 1351abf2add6SEd Tanous const std::variant<std::string> &resp) { 13521da66f75SEd Tanous if (ec) 13531da66f75SEd Tanous { 1354abf2add6SEd Tanous BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message(); 1355f12894f8SJason M. Bills messages::internalError(asyncResp->res); 13561da66f75SEd Tanous return; 13571da66f75SEd Tanous } 1358abf2add6SEd Tanous const std::string *log = std::get_if<std::string>(&resp); 13591da66f75SEd Tanous if (log == nullptr) 13601da66f75SEd Tanous { 1361f12894f8SJason M. Bills messages::internalError(asyncResp->res); 13621da66f75SEd Tanous return; 13631da66f75SEd Tanous } 13641da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 13651da66f75SEd Tanous if (j.is_discarded()) 13661da66f75SEd Tanous { 1367f12894f8SJason M. Bills messages::internalError(asyncResp->res); 13681da66f75SEd Tanous return; 13691da66f75SEd Tanous } 13701da66f75SEd Tanous std::string t = getLogCreatedTime(j); 1371e1f26343SJason M. Bills asyncResp->res.jsonValue = { 1372*cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 1373abf2add6SEd Tanous {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 13741da66f75SEd Tanous {"@odata.id", 1375d53dd41fSJason M. Bills "/redfish/v1/Systems/system/LogServices/CpuLog/Entries/" + 13764ed77cd5SEd Tanous std::to_string(logId)}, 13771da66f75SEd Tanous {"Name", "CPU Debug Log"}, 13784ed77cd5SEd Tanous {"Id", logId}, 13791da66f75SEd Tanous {"EntryType", "Oem"}, 13801da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 13811da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 13821da66f75SEd Tanous {"Created", std::move(t)}}; 13831da66f75SEd Tanous }; 13841da66f75SEd Tanous crow::connections::systemBus->async_method_call( 13854ed77cd5SEd Tanous std::move(getStoredLogCallback), cpuLogObject, 13864ed77cd5SEd Tanous cpuLogPath + std::string("/") + std::to_string(logId), 13874ed77cd5SEd Tanous "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log"); 13881da66f75SEd Tanous } 13891da66f75SEd Tanous }; 13901da66f75SEd Tanous 1391e1f26343SJason M. Bills class ImmediateCPULog : public Node 13921da66f75SEd Tanous { 13931da66f75SEd Tanous public: 1394e1f26343SJason M. Bills ImmediateCPULog(CrowApp &app) : 1395d53dd41fSJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/CpuLog/Actions/Oem/" 1396e1f26343SJason M. Bills "CpuLog.Immediate/") 13971da66f75SEd Tanous { 13981da66f75SEd Tanous entityPrivileges = { 1399e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1400e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1401e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1402e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1403e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1404e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 14051da66f75SEd Tanous } 14061da66f75SEd Tanous 14071da66f75SEd Tanous private: 14081da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 14091da66f75SEd Tanous const std::vector<std::string> ¶ms) override 14101da66f75SEd Tanous { 1411e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 14121da66f75SEd Tanous static std::unique_ptr<sdbusplus::bus::match::match> 14131da66f75SEd Tanous immediateLogMatcher; 14141da66f75SEd Tanous 14151da66f75SEd Tanous // Only allow one Immediate Log request at a time 14161da66f75SEd Tanous if (immediateLogMatcher != nullptr) 14171da66f75SEd Tanous { 1418e1f26343SJason M. Bills asyncResp->res.addHeader("Retry-After", "30"); 1419f12894f8SJason M. Bills messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); 14201da66f75SEd Tanous return; 14211da66f75SEd Tanous } 14221da66f75SEd Tanous // Make this static so it survives outside this method 14231da66f75SEd Tanous static boost::asio::deadline_timer timeout(*req.ioService); 14241da66f75SEd Tanous 14251da66f75SEd Tanous timeout.expires_from_now(boost::posix_time::seconds(30)); 1426e1f26343SJason M. Bills timeout.async_wait([asyncResp](const boost::system::error_code &ec) { 14271da66f75SEd Tanous immediateLogMatcher = nullptr; 14281da66f75SEd Tanous if (ec) 14291da66f75SEd Tanous { 14301da66f75SEd Tanous // operation_aborted is expected if timer is canceled before 14311da66f75SEd Tanous // completion. 14321da66f75SEd Tanous if (ec != boost::asio::error::operation_aborted) 14331da66f75SEd Tanous { 14341da66f75SEd Tanous BMCWEB_LOG_ERROR << "Async_wait failed " << ec; 14351da66f75SEd Tanous } 14361da66f75SEd Tanous return; 14371da66f75SEd Tanous } 14381da66f75SEd Tanous BMCWEB_LOG_ERROR << "Timed out waiting for immediate log"; 14391da66f75SEd Tanous 1440f12894f8SJason M. Bills messages::internalError(asyncResp->res); 14411da66f75SEd Tanous }); 14421da66f75SEd Tanous 1443e1f26343SJason M. Bills auto immediateLogMatcherCallback = [asyncResp]( 14441da66f75SEd Tanous sdbusplus::message::message &m) { 14451da66f75SEd Tanous BMCWEB_LOG_DEBUG << "Immediate log available match fired"; 14461da66f75SEd Tanous boost::system::error_code ec; 14471da66f75SEd Tanous timeout.cancel(ec); 14481da66f75SEd Tanous if (ec) 14491da66f75SEd Tanous { 14501da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " << ec; 14511da66f75SEd Tanous } 14524ed77cd5SEd Tanous sdbusplus::message::object_path objPath; 14531da66f75SEd Tanous boost::container::flat_map< 1454abf2add6SEd Tanous std::string, boost::container::flat_map< 1455abf2add6SEd Tanous std::string, std::variant<std::string>>> 14564ed77cd5SEd Tanous interfacesAdded; 14574ed77cd5SEd Tanous m.read(objPath, interfacesAdded); 1458abf2add6SEd Tanous const std::string *log = std::get_if<std::string>( 14591b6b96c5SEd Tanous &interfacesAdded[cpuLogInterface]["Log"]); 14601da66f75SEd Tanous if (log == nullptr) 14611da66f75SEd Tanous { 1462f12894f8SJason M. Bills messages::internalError(asyncResp->res); 14631da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 14641da66f75SEd Tanous // match object inside which this lambda is executing. Once it 14651da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 14661da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 14671da66f75SEd Tanous // be the last thing done. 14681da66f75SEd Tanous immediateLogMatcher = nullptr; 14691da66f75SEd Tanous return; 14701da66f75SEd Tanous } 14711da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 14721da66f75SEd Tanous if (j.is_discarded()) 14731da66f75SEd Tanous { 1474f12894f8SJason M. Bills messages::internalError(asyncResp->res); 14751da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 14761da66f75SEd Tanous // match object inside which this lambda is executing. Once it 14771da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 14781da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 14791da66f75SEd Tanous // be the last thing done. 14801da66f75SEd Tanous immediateLogMatcher = nullptr; 14811da66f75SEd Tanous return; 14821da66f75SEd Tanous } 14831da66f75SEd Tanous std::string t = getLogCreatedTime(j); 1484e1f26343SJason M. Bills asyncResp->res.jsonValue = { 1485*cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 14861da66f75SEd Tanous {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 14871da66f75SEd Tanous {"Name", "CPU Debug Log"}, 14881da66f75SEd Tanous {"EntryType", "Oem"}, 14891da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 14901da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 14911da66f75SEd Tanous {"Created", std::move(t)}}; 14921da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 14931da66f75SEd Tanous // match object inside which this lambda is executing. Once it is 14941da66f75SEd Tanous // set to nullptr, the match object will be destroyed and the lambda 14951da66f75SEd Tanous // will lose its context, including res, so it needs to be the last 14961da66f75SEd Tanous // thing done. 14971da66f75SEd Tanous immediateLogMatcher = nullptr; 14981da66f75SEd Tanous }; 14991da66f75SEd Tanous immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>( 15001da66f75SEd Tanous *crow::connections::systemBus, 15011da66f75SEd Tanous sdbusplus::bus::match::rules::interfacesAdded() + 15024ed77cd5SEd Tanous sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath), 15031da66f75SEd Tanous std::move(immediateLogMatcherCallback)); 15041da66f75SEd Tanous 15051da66f75SEd Tanous auto generateImmediateLogCallback = 1506e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 15071da66f75SEd Tanous const std::string &resp) { 15081da66f75SEd Tanous if (ec) 15091da66f75SEd Tanous { 15101da66f75SEd Tanous if (ec.value() == 15111da66f75SEd Tanous boost::system::errc::operation_not_supported) 15121da66f75SEd Tanous { 1513f12894f8SJason M. Bills messages::resourceInStandby(asyncResp->res); 15141da66f75SEd Tanous } 15151da66f75SEd Tanous else 15161da66f75SEd Tanous { 1517f12894f8SJason M. Bills messages::internalError(asyncResp->res); 15181da66f75SEd Tanous } 15191da66f75SEd Tanous boost::system::error_code timeoutec; 15201da66f75SEd Tanous timeout.cancel(timeoutec); 15211da66f75SEd Tanous if (timeoutec) 15221da66f75SEd Tanous { 15231da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " 15241da66f75SEd Tanous << timeoutec; 15251da66f75SEd Tanous } 15261da66f75SEd Tanous immediateLogMatcher = nullptr; 15271da66f75SEd Tanous return; 15281da66f75SEd Tanous } 15291da66f75SEd Tanous }; 15301da66f75SEd Tanous crow::connections::systemBus->async_method_call( 15314ed77cd5SEd Tanous std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath, 15324ed77cd5SEd Tanous cpuLogImmediateInterface, "GenerateImmediateLog"); 15331da66f75SEd Tanous } 15341da66f75SEd Tanous }; 15351da66f75SEd Tanous 1536e1f26343SJason M. Bills class SendRawPECI : public Node 15371da66f75SEd Tanous { 15381da66f75SEd Tanous public: 1539e1f26343SJason M. Bills SendRawPECI(CrowApp &app) : 1540d53dd41fSJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/CpuLog/Actions/Oem/" 1541e1f26343SJason M. Bills "CpuLog.SendRawPeci/") 15421da66f75SEd Tanous { 15431da66f75SEd Tanous entityPrivileges = { 15441da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 15451da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 15461da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 15471da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 15481da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 15491da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 15501da66f75SEd Tanous } 15511da66f75SEd Tanous 15521da66f75SEd Tanous private: 15531da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 15541da66f75SEd Tanous const std::vector<std::string> ¶ms) override 15551da66f75SEd Tanous { 1556e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1557b1556427SEd Tanous uint8_t clientAddress = 0; 1558b1556427SEd Tanous uint8_t readLength = 0; 15591da66f75SEd Tanous std::vector<uint8_t> peciCommand; 1560b1556427SEd Tanous if (!json_util::readJson(req, res, "ClientAddress", clientAddress, 1561b1556427SEd Tanous "ReadLength", readLength, "PECICommand", 1562b1556427SEd Tanous peciCommand)) 15631da66f75SEd Tanous { 15641da66f75SEd Tanous return; 15651da66f75SEd Tanous } 1566b1556427SEd Tanous 15671da66f75SEd Tanous // Callback to return the Raw PECI response 1568e1f26343SJason M. Bills auto sendRawPECICallback = 1569e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 15701da66f75SEd Tanous const std::vector<uint8_t> &resp) { 15711da66f75SEd Tanous if (ec) 15721da66f75SEd Tanous { 15731da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to send PECI command ec: " 15741da66f75SEd Tanous << ec.message(); 1575f12894f8SJason M. Bills messages::internalError(asyncResp->res); 15761da66f75SEd Tanous return; 15771da66f75SEd Tanous } 1578e1f26343SJason M. Bills asyncResp->res.jsonValue = {{"Name", "PECI Command Response"}, 15791da66f75SEd Tanous {"PECIResponse", resp}}; 15801da66f75SEd Tanous }; 15811da66f75SEd Tanous // Call the SendRawPECI command with the provided data 15821da66f75SEd Tanous crow::connections::systemBus->async_method_call( 1583e1f26343SJason M. Bills std::move(sendRawPECICallback), cpuLogObject, cpuLogPath, 1584e1f26343SJason M. Bills cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength, 15854ed77cd5SEd Tanous peciCommand); 15861da66f75SEd Tanous } 15871da66f75SEd Tanous }; 15881da66f75SEd Tanous 1589*cb92c03bSAndrew Geissler /** 1590*cb92c03bSAndrew Geissler * DBusLogServiceActionsClear class supports POST method for ClearLog action. 1591*cb92c03bSAndrew Geissler */ 1592*cb92c03bSAndrew Geissler class DBusLogServiceActionsClear : public Node 1593*cb92c03bSAndrew Geissler { 1594*cb92c03bSAndrew Geissler public: 1595*cb92c03bSAndrew Geissler DBusLogServiceActionsClear(CrowApp &app) : 1596*cb92c03bSAndrew Geissler Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/" 1597*cb92c03bSAndrew Geissler "LogService.Reset") 1598*cb92c03bSAndrew Geissler { 1599*cb92c03bSAndrew Geissler entityPrivileges = { 1600*cb92c03bSAndrew Geissler {boost::beast::http::verb::get, {{"Login"}}}, 1601*cb92c03bSAndrew Geissler {boost::beast::http::verb::head, {{"Login"}}}, 1602*cb92c03bSAndrew Geissler {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1603*cb92c03bSAndrew Geissler {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1604*cb92c03bSAndrew Geissler {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1605*cb92c03bSAndrew Geissler {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1606*cb92c03bSAndrew Geissler } 1607*cb92c03bSAndrew Geissler 1608*cb92c03bSAndrew Geissler private: 1609*cb92c03bSAndrew Geissler /** 1610*cb92c03bSAndrew Geissler * Function handles POST method request. 1611*cb92c03bSAndrew Geissler * The Clear Log actions does not require any parameter.The action deletes 1612*cb92c03bSAndrew Geissler * all entries found in the Entries collection for this Log Service. 1613*cb92c03bSAndrew Geissler */ 1614*cb92c03bSAndrew Geissler void doPost(crow::Response &res, const crow::Request &req, 1615*cb92c03bSAndrew Geissler const std::vector<std::string> ¶ms) override 1616*cb92c03bSAndrew Geissler { 1617*cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "Do delete all entries."; 1618*cb92c03bSAndrew Geissler 1619*cb92c03bSAndrew Geissler auto asyncResp = std::make_shared<AsyncResp>(res); 1620*cb92c03bSAndrew Geissler // Process response from Logging service. 1621*cb92c03bSAndrew Geissler auto resp_handler = [asyncResp](const boost::system::error_code ec) { 1622*cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done"; 1623*cb92c03bSAndrew Geissler if (ec) 1624*cb92c03bSAndrew Geissler { 1625*cb92c03bSAndrew Geissler // TODO Handle for specific error code 1626*cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec; 1627*cb92c03bSAndrew Geissler asyncResp->res.result( 1628*cb92c03bSAndrew Geissler boost::beast::http::status::internal_server_error); 1629*cb92c03bSAndrew Geissler return; 1630*cb92c03bSAndrew Geissler } 1631*cb92c03bSAndrew Geissler 1632*cb92c03bSAndrew Geissler asyncResp->res.result(boost::beast::http::status::no_content); 1633*cb92c03bSAndrew Geissler }; 1634*cb92c03bSAndrew Geissler 1635*cb92c03bSAndrew Geissler // Make call to Logging service to request Clear Log 1636*cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 1637*cb92c03bSAndrew Geissler resp_handler, "xyz.openbmc_project.Logging", 1638*cb92c03bSAndrew Geissler "/xyz/openbmc_project/logging", 1639*cb92c03bSAndrew Geissler "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); 1640*cb92c03bSAndrew Geissler } 1641*cb92c03bSAndrew Geissler }; 16421da66f75SEd Tanous } // namespace redfish 1643