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> 24e1f26343SJason M. Bills #include <boost/utility/string_view.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 4116428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal, 4216428a1aSJason M. Bills const boost::string_view &field, 4316428a1aSJason M. Bills boost::string_view &contents) 4416428a1aSJason M. Bills { 4516428a1aSJason M. Bills const char *data = nullptr; 4616428a1aSJason M. Bills size_t length = 0; 4716428a1aSJason M. Bills int ret = 0; 4816428a1aSJason M. Bills // Get the metadata from the requested field of the journal entry 4916428a1aSJason M. Bills ret = sd_journal_get_data(journal, field.data(), (const void **)&data, 5016428a1aSJason M. Bills &length); 5116428a1aSJason M. Bills if (ret < 0) 5216428a1aSJason M. Bills { 5316428a1aSJason M. Bills return ret; 5416428a1aSJason M. Bills } 5516428a1aSJason M. Bills contents = boost::string_view(data, length); 5616428a1aSJason M. Bills // Only use the content after the "=" character. 5716428a1aSJason M. Bills contents.remove_prefix(std::min(contents.find("=") + 1, contents.size())); 5816428a1aSJason M. Bills return ret; 5916428a1aSJason M. Bills } 6016428a1aSJason M. Bills 6116428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal, 6216428a1aSJason M. Bills const boost::string_view &field, const int &base, 6316428a1aSJason M. Bills int &contents) 6416428a1aSJason M. Bills { 6516428a1aSJason M. Bills int ret = 0; 6616428a1aSJason M. Bills boost::string_view metadata; 6716428a1aSJason M. Bills // Get the metadata from the requested field of the journal entry 6816428a1aSJason M. Bills ret = getJournalMetadata(journal, field, metadata); 6916428a1aSJason M. Bills if (ret < 0) 7016428a1aSJason M. Bills { 7116428a1aSJason M. Bills return ret; 7216428a1aSJason M. Bills } 7316428a1aSJason M. Bills contents = strtol(metadata.data(), nullptr, base); 7416428a1aSJason M. Bills return ret; 7516428a1aSJason M. Bills } 7616428a1aSJason M. Bills 7716428a1aSJason M. Bills static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp) 7816428a1aSJason M. Bills { 7916428a1aSJason M. Bills int ret = 0; 8016428a1aSJason M. Bills uint64_t timestamp = 0; 8116428a1aSJason M. Bills ret = sd_journal_get_realtime_usec(journal, ×tamp); 8216428a1aSJason M. Bills if (ret < 0) 8316428a1aSJason M. Bills { 8416428a1aSJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 8516428a1aSJason M. Bills << strerror(-ret); 8616428a1aSJason M. Bills return false; 8716428a1aSJason M. Bills } 8816428a1aSJason M. Bills time_t t = 8916428a1aSJason M. Bills static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s 9016428a1aSJason M. Bills struct tm *loctime = localtime(&t); 9116428a1aSJason M. Bills char entryTime[64] = {}; 9216428a1aSJason M. Bills if (NULL != loctime) 9316428a1aSJason M. Bills { 9416428a1aSJason M. Bills strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime); 9516428a1aSJason M. Bills } 9616428a1aSJason M. Bills // Insert the ':' into the timezone 9716428a1aSJason M. Bills boost::string_view t1(entryTime); 9816428a1aSJason M. Bills boost::string_view t2(entryTime); 9916428a1aSJason M. Bills if (t1.size() > 2 && t2.size() > 2) 10016428a1aSJason M. Bills { 10116428a1aSJason M. Bills t1.remove_suffix(2); 10216428a1aSJason M. Bills t2.remove_prefix(t2.size() - 2); 10316428a1aSJason M. Bills } 10416428a1aSJason M. Bills entryTimestamp = t1.to_string() + ":" + t2.to_string(); 10516428a1aSJason M. Bills return true; 10616428a1aSJason M. Bills } 10716428a1aSJason M. Bills 10816428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req, 10916428a1aSJason M. Bills long &skip) 11016428a1aSJason M. Bills { 11116428a1aSJason M. Bills char *skipParam = req.urlParams.get("$skip"); 11216428a1aSJason M. Bills if (skipParam != nullptr) 11316428a1aSJason M. Bills { 11416428a1aSJason M. Bills char *ptr = nullptr; 11516428a1aSJason M. Bills skip = std::strtol(skipParam, &ptr, 10); 11616428a1aSJason M. Bills if (*skipParam == '\0' || *ptr != '\0') 11716428a1aSJason M. Bills { 11816428a1aSJason M. Bills 11916428a1aSJason M. Bills messages::queryParameterValueTypeError(res, std::string(skipParam), 12016428a1aSJason M. Bills "$skip"); 12116428a1aSJason M. Bills return false; 12216428a1aSJason M. Bills } 12316428a1aSJason M. Bills if (skip < 0) 12416428a1aSJason M. Bills { 12516428a1aSJason M. Bills 12616428a1aSJason M. Bills messages::queryParameterOutOfRange(res, std::to_string(skip), 12716428a1aSJason M. Bills "$skip", "greater than 0"); 12816428a1aSJason M. Bills return false; 12916428a1aSJason M. Bills } 13016428a1aSJason M. Bills } 13116428a1aSJason M. Bills return true; 13216428a1aSJason M. Bills } 13316428a1aSJason M. Bills 13416428a1aSJason M. Bills static constexpr const long maxEntriesPerPage = 1000; 13516428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req, 13616428a1aSJason M. Bills long &top) 13716428a1aSJason M. Bills { 13816428a1aSJason M. Bills char *topParam = req.urlParams.get("$top"); 13916428a1aSJason M. Bills if (topParam != nullptr) 14016428a1aSJason M. Bills { 14116428a1aSJason M. Bills char *ptr = nullptr; 14216428a1aSJason M. Bills top = std::strtol(topParam, &ptr, 10); 14316428a1aSJason M. Bills if (*topParam == '\0' || *ptr != '\0') 14416428a1aSJason M. Bills { 14516428a1aSJason M. Bills messages::queryParameterValueTypeError(res, std::string(topParam), 14616428a1aSJason M. Bills "$top"); 14716428a1aSJason M. Bills return false; 14816428a1aSJason M. Bills } 14916428a1aSJason M. Bills if (top < 1 || top > maxEntriesPerPage) 15016428a1aSJason M. Bills { 15116428a1aSJason M. Bills 15216428a1aSJason M. Bills messages::queryParameterOutOfRange( 15316428a1aSJason M. Bills res, std::to_string(top), "$top", 15416428a1aSJason M. Bills "1-" + std::to_string(maxEntriesPerPage)); 15516428a1aSJason M. Bills return false; 15616428a1aSJason M. Bills } 15716428a1aSJason M. Bills } 15816428a1aSJason M. Bills return true; 15916428a1aSJason M. Bills } 16016428a1aSJason M. Bills 16116428a1aSJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID) 16216428a1aSJason M. Bills { 16316428a1aSJason M. Bills int ret = 0; 16416428a1aSJason M. Bills static uint64_t prevTs = 0; 16516428a1aSJason M. Bills static int index = 0; 16616428a1aSJason M. Bills // Get the entry timestamp 16716428a1aSJason M. Bills uint64_t curTs = 0; 16816428a1aSJason M. Bills ret = sd_journal_get_realtime_usec(journal, &curTs); 16916428a1aSJason M. Bills if (ret < 0) 17016428a1aSJason M. Bills { 17116428a1aSJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 17216428a1aSJason M. Bills << strerror(-ret); 17316428a1aSJason M. Bills return false; 17416428a1aSJason M. Bills } 17516428a1aSJason M. Bills // If the timestamp isn't unique, increment the index 17616428a1aSJason M. Bills if (curTs == prevTs) 17716428a1aSJason M. Bills { 17816428a1aSJason M. Bills index++; 17916428a1aSJason M. Bills } 18016428a1aSJason M. Bills else 18116428a1aSJason M. Bills { 18216428a1aSJason M. Bills // Otherwise, reset it 18316428a1aSJason M. Bills index = 0; 18416428a1aSJason M. Bills } 18516428a1aSJason M. Bills // Save the timestamp 18616428a1aSJason M. Bills prevTs = curTs; 18716428a1aSJason M. Bills 18816428a1aSJason M. Bills entryID = std::to_string(curTs); 18916428a1aSJason M. Bills if (index > 0) 19016428a1aSJason M. Bills { 19116428a1aSJason M. Bills entryID += "_" + std::to_string(index); 19216428a1aSJason M. Bills } 19316428a1aSJason M. Bills return true; 19416428a1aSJason M. Bills } 19516428a1aSJason M. Bills 19616428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID, 19716428a1aSJason M. Bills uint64_t ×tamp, uint16_t &index) 19816428a1aSJason M. Bills { 19916428a1aSJason M. Bills if (entryID.empty()) 20016428a1aSJason M. Bills { 20116428a1aSJason M. Bills return false; 20216428a1aSJason M. Bills } 20316428a1aSJason M. Bills // Convert the unique ID back to a timestamp to find the entry 20416428a1aSJason M. Bills boost::string_view tsStr(entryID); 20516428a1aSJason M. Bills 20616428a1aSJason M. Bills auto underscorePos = tsStr.find("_"); 20716428a1aSJason M. Bills if (underscorePos != tsStr.npos) 20816428a1aSJason M. Bills { 20916428a1aSJason M. Bills // Timestamp has an index 21016428a1aSJason M. Bills tsStr.remove_suffix(tsStr.size() - underscorePos); 21116428a1aSJason M. Bills boost::string_view indexStr(entryID); 21216428a1aSJason M. Bills indexStr.remove_prefix(underscorePos + 1); 21316428a1aSJason M. Bills std::size_t pos; 21416428a1aSJason M. Bills try 21516428a1aSJason M. Bills { 21616428a1aSJason M. Bills index = std::stoul(indexStr.to_string(), &pos); 21716428a1aSJason M. Bills } 21816428a1aSJason M. Bills catch (std::invalid_argument) 21916428a1aSJason M. Bills { 22016428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 22116428a1aSJason M. Bills return false; 22216428a1aSJason M. Bills } 22316428a1aSJason M. Bills catch (std::out_of_range) 22416428a1aSJason M. Bills { 22516428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 22616428a1aSJason M. Bills return false; 22716428a1aSJason M. Bills } 22816428a1aSJason M. Bills if (pos != indexStr.size()) 22916428a1aSJason M. Bills { 23016428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 23116428a1aSJason M. Bills return false; 23216428a1aSJason M. Bills } 23316428a1aSJason M. Bills } 23416428a1aSJason M. Bills // Timestamp has no index 23516428a1aSJason M. Bills std::size_t pos; 23616428a1aSJason M. Bills try 23716428a1aSJason M. Bills { 23816428a1aSJason M. Bills timestamp = std::stoull(tsStr.to_string(), &pos); 23916428a1aSJason M. Bills } 24016428a1aSJason M. Bills catch (std::invalid_argument) 24116428a1aSJason M. Bills { 24216428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 24316428a1aSJason M. Bills return false; 24416428a1aSJason M. Bills } 24516428a1aSJason M. Bills catch (std::out_of_range) 24616428a1aSJason M. Bills { 24716428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 24816428a1aSJason M. Bills return false; 24916428a1aSJason M. Bills } 25016428a1aSJason M. Bills if (pos != tsStr.size()) 25116428a1aSJason M. Bills { 25216428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 25316428a1aSJason M. Bills return false; 25416428a1aSJason M. Bills } 25516428a1aSJason M. Bills return true; 25616428a1aSJason M. Bills } 25716428a1aSJason M. Bills 258c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node 2591da66f75SEd Tanous { 2601da66f75SEd Tanous public: 2611da66f75SEd Tanous template <typename CrowApp> 262c4bf6374SJason M. Bills SystemLogServiceCollection(CrowApp &app) : 263029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/") 264c4bf6374SJason M. Bills { 265c4bf6374SJason M. Bills entityPrivileges = { 266c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 267c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 268c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 269c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 270c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 271c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 272c4bf6374SJason M. Bills } 273c4bf6374SJason M. Bills 274c4bf6374SJason M. Bills private: 275c4bf6374SJason M. Bills /** 276c4bf6374SJason M. Bills * Functions triggers appropriate requests on DBus 277c4bf6374SJason M. Bills */ 278c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 279c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 280c4bf6374SJason M. Bills { 281c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 282c4bf6374SJason M. Bills // Collections don't include the static data added by SubRoute because 283c4bf6374SJason M. Bills // it has a duplicate entry for members 284c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 285c4bf6374SJason M. Bills "#LogServiceCollection.LogServiceCollection"; 286c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 287c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection"; 288c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 289029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices"; 290c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "System Log Services Collection"; 291c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = 292c4bf6374SJason M. Bills "Collection of LogServices for this Computer System"; 293c4bf6374SJason M. Bills nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; 294c4bf6374SJason M. Bills logServiceArray = nlohmann::json::array(); 295029573d4SEd Tanous logServiceArray.push_back( 296029573d4SEd Tanous {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}}); 297c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 298c4bf6374SJason M. Bills logServiceArray.size(); 299c4bf6374SJason M. Bills } 300c4bf6374SJason M. Bills }; 301c4bf6374SJason M. Bills 302c4bf6374SJason M. Bills class EventLogService : public Node 303c4bf6374SJason M. Bills { 304c4bf6374SJason M. Bills public: 305c4bf6374SJason M. Bills template <typename CrowApp> 306c4bf6374SJason M. Bills EventLogService(CrowApp &app) : 307029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/") 308c4bf6374SJason M. Bills { 309c4bf6374SJason M. Bills entityPrivileges = { 310c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 311c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 312c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 313c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 314c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 315c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 316c4bf6374SJason M. Bills } 317c4bf6374SJason M. Bills 318c4bf6374SJason M. Bills private: 319c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 320c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 321c4bf6374SJason M. Bills { 322c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 323c4bf6374SJason M. Bills 324c4bf6374SJason M. Bills const std::string &name = params[0]; 325c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 326029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog"; 327c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 328c4bf6374SJason M. Bills "#LogService.v1_1_0.LogService"; 329c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 330c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 331c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "Event Log Service"; 332c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = "System Event Log Service"; 333c4bf6374SJason M. Bills asyncResp->res.jsonValue["Id"] = "Event Log"; 334c4bf6374SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 335c4bf6374SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 336c4bf6374SJason M. Bills {"@odata.id", 337029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}}; 338c4bf6374SJason M. Bills } 339c4bf6374SJason M. Bills }; 340c4bf6374SJason M. Bills 341029573d4SEd Tanous static int fillEventLogEntryJson(const std::string &bmcLogEntryID, 342c4bf6374SJason M. Bills const boost::string_view &messageID, 343c4bf6374SJason M. Bills sd_journal *journal, 344c4bf6374SJason M. Bills nlohmann::json &bmcLogEntryJson) 345c4bf6374SJason M. Bills { 346c4bf6374SJason M. Bills // Get the Log Entry contents 347c4bf6374SJason M. Bills int ret = 0; 348c4bf6374SJason M. Bills 349c4bf6374SJason M. Bills boost::string_view msg; 350c4bf6374SJason M. Bills ret = getJournalMetadata(journal, "MESSAGE", msg); 351c4bf6374SJason M. Bills if (ret < 0) 352c4bf6374SJason M. Bills { 353c4bf6374SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret); 354c4bf6374SJason M. Bills return 1; 355c4bf6374SJason M. Bills } 356c4bf6374SJason M. Bills 357c4bf6374SJason M. Bills // Get the severity from the PRIORITY field 358c4bf6374SJason M. Bills int severity = 8; // Default to an invalid priority 359c4bf6374SJason M. Bills ret = getJournalMetadata(journal, "PRIORITY", 10, severity); 360c4bf6374SJason M. Bills if (ret < 0) 361c4bf6374SJason M. Bills { 362c4bf6374SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret); 363c4bf6374SJason M. Bills return 1; 364c4bf6374SJason M. Bills } 365c4bf6374SJason M. Bills 366c4bf6374SJason M. Bills // Get the MessageArgs from the journal entry by finding all of the 367c4bf6374SJason M. Bills // REDFISH_MESSAGE_ARG_x fields 368c4bf6374SJason M. Bills const void *data; 369c4bf6374SJason M. Bills size_t length; 370c4bf6374SJason M. Bills std::vector<std::string> messageArgs; 371c4bf6374SJason M. Bills SD_JOURNAL_FOREACH_DATA(journal, data, length) 372c4bf6374SJason M. Bills { 373c4bf6374SJason M. Bills boost::string_view field(static_cast<const char *>(data), length); 374c4bf6374SJason M. Bills if (field.starts_with("REDFISH_MESSAGE_ARG_")) 375c4bf6374SJason M. Bills { 376c4bf6374SJason M. Bills // Get the Arg number from the field name 377c4bf6374SJason M. Bills field.remove_prefix(sizeof("REDFISH_MESSAGE_ARG_") - 1); 378c4bf6374SJason M. Bills if (field.empty()) 379c4bf6374SJason M. Bills { 380c4bf6374SJason M. Bills continue; 381c4bf6374SJason M. Bills } 382c4bf6374SJason M. Bills int argNum = std::strtoul(field.data(), nullptr, 10); 383c4bf6374SJason M. Bills if (argNum == 0) 384c4bf6374SJason M. Bills { 385c4bf6374SJason M. Bills continue; 386c4bf6374SJason M. Bills } 387c4bf6374SJason M. Bills // Get the Arg value after the "=" character. 388c4bf6374SJason M. Bills field.remove_prefix(std::min(field.find("=") + 1, field.size())); 389c4bf6374SJason M. Bills // Make sure we have enough space in messageArgs 390c4bf6374SJason M. Bills if (argNum > messageArgs.size()) 391c4bf6374SJason M. Bills { 392c4bf6374SJason M. Bills messageArgs.resize(argNum); 393c4bf6374SJason M. Bills } 394c4bf6374SJason M. Bills messageArgs[argNum - 1] = field.to_string(); 395c4bf6374SJason M. Bills } 396c4bf6374SJason M. Bills } 397c4bf6374SJason M. Bills 398c4bf6374SJason M. Bills // Get the Created time from the timestamp 399c4bf6374SJason M. Bills std::string entryTimeStr; 400c4bf6374SJason M. Bills if (!getEntryTimestamp(journal, entryTimeStr)) 401c4bf6374SJason M. Bills { 402c4bf6374SJason M. Bills return 1; 403c4bf6374SJason M. Bills } 404c4bf6374SJason M. Bills 405c4bf6374SJason M. Bills // Fill in the log entry with the gathered data 406c4bf6374SJason M. Bills bmcLogEntryJson = { 407c4bf6374SJason M. Bills {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 408c4bf6374SJason M. Bills {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 409029573d4SEd Tanous {"@odata.id", 410029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + 411029573d4SEd Tanous bmcLogEntryID}, 412c4bf6374SJason M. Bills {"Name", "System Event Log Entry"}, 413c4bf6374SJason M. Bills {"Id", bmcLogEntryID}, 414c4bf6374SJason M. Bills {"Message", msg}, 415c4bf6374SJason M. Bills {"MessageId", messageID}, 416c4bf6374SJason M. Bills {"MessageArgs", std::move(messageArgs)}, 417c4bf6374SJason M. Bills {"EntryType", "Event"}, 418c4bf6374SJason M. Bills {"Severity", 419c4bf6374SJason M. Bills severity <= 2 ? "Critical" 420c4bf6374SJason M. Bills : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""}, 421c4bf6374SJason M. Bills {"Created", std::move(entryTimeStr)}}; 422c4bf6374SJason M. Bills return 0; 423c4bf6374SJason M. Bills } 424c4bf6374SJason M. Bills 425c4bf6374SJason M. Bills class EventLogEntryCollection : public Node 426c4bf6374SJason M. Bills { 427c4bf6374SJason M. Bills public: 428c4bf6374SJason M. Bills template <typename CrowApp> 429c4bf6374SJason M. Bills EventLogEntryCollection(CrowApp &app) : 430029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/") 431c4bf6374SJason M. Bills { 432c4bf6374SJason M. Bills entityPrivileges = { 433c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 434c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 435c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 436c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 437c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 438c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 439c4bf6374SJason M. Bills } 440c4bf6374SJason M. Bills 441c4bf6374SJason M. Bills private: 442c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 443c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 444c4bf6374SJason M. Bills { 445c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 446c4bf6374SJason M. Bills long skip = 0; 447c4bf6374SJason M. Bills long top = maxEntriesPerPage; // Show max entries by default 448c4bf6374SJason M. Bills if (!getSkipParam(asyncResp->res, req, skip)) 449c4bf6374SJason M. Bills { 450c4bf6374SJason M. Bills return; 451c4bf6374SJason M. Bills } 452c4bf6374SJason M. Bills if (!getTopParam(asyncResp->res, req, top)) 453c4bf6374SJason M. Bills { 454c4bf6374SJason M. Bills return; 455c4bf6374SJason M. Bills } 456c4bf6374SJason M. Bills // Collections don't include the static data added by SubRoute because 457c4bf6374SJason M. Bills // it has a duplicate entry for members 458c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 459c4bf6374SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 460c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 461c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection"; 462c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 463029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries"; 464c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 465c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = 466c4bf6374SJason M. Bills "Collection of System Event Log Entries"; 467c4bf6374SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 468c4bf6374SJason M. Bills logEntryArray = nlohmann::json::array(); 469c4bf6374SJason M. Bills 470c4bf6374SJason M. Bills // Go through the journal and create a unique ID for each entry 471c4bf6374SJason M. Bills sd_journal *journalTmp = nullptr; 472c4bf6374SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 473c4bf6374SJason M. Bills if (ret < 0) 474c4bf6374SJason M. Bills { 475c4bf6374SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 476c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 477c4bf6374SJason M. Bills return; 478c4bf6374SJason M. Bills } 479c4bf6374SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 480c4bf6374SJason M. Bills journalTmp, sd_journal_close); 481c4bf6374SJason M. Bills journalTmp = nullptr; 482c4bf6374SJason M. Bills uint64_t entryCount = 0; 483c4bf6374SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 484c4bf6374SJason M. Bills { 485c4bf6374SJason M. Bills // Look for only journal entries that contain a REDFISH_MESSAGE_ID 486c4bf6374SJason M. Bills // field 487c4bf6374SJason M. Bills boost::string_view messageID; 488c4bf6374SJason M. Bills ret = getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", 489c4bf6374SJason M. Bills messageID); 490c4bf6374SJason M. Bills if (ret < 0) 491c4bf6374SJason M. Bills { 492c4bf6374SJason M. Bills continue; 493c4bf6374SJason M. Bills } 494c4bf6374SJason M. Bills 495c4bf6374SJason M. Bills entryCount++; 496c4bf6374SJason M. Bills // Handle paging using skip (number of entries to skip from the 497c4bf6374SJason M. Bills // start) and top (number of entries to display) 498c4bf6374SJason M. Bills if (entryCount <= skip || entryCount > skip + top) 499c4bf6374SJason M. Bills { 500c4bf6374SJason M. Bills continue; 501c4bf6374SJason M. Bills } 502c4bf6374SJason M. Bills 503c4bf6374SJason M. Bills std::string idStr; 504c4bf6374SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr)) 505c4bf6374SJason M. Bills { 506c4bf6374SJason M. Bills continue; 507c4bf6374SJason M. Bills } 508c4bf6374SJason M. Bills 509c4bf6374SJason M. Bills logEntryArray.push_back({}); 510c4bf6374SJason M. Bills nlohmann::json &bmcLogEntry = logEntryArray.back(); 511029573d4SEd Tanous if (fillEventLogEntryJson(idStr, messageID, journal.get(), 512c4bf6374SJason M. Bills bmcLogEntry) != 0) 513c4bf6374SJason M. Bills { 514c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 515c4bf6374SJason M. Bills return; 516c4bf6374SJason M. Bills } 517c4bf6374SJason M. Bills } 518c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 519c4bf6374SJason M. Bills if (skip + top < entryCount) 520c4bf6374SJason M. Bills { 521c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 522c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" + 523c4bf6374SJason M. Bills std::to_string(skip + top); 524c4bf6374SJason M. Bills } 525c4bf6374SJason M. Bills } 526c4bf6374SJason M. Bills }; 527c4bf6374SJason M. Bills 528c4bf6374SJason M. Bills class EventLogEntry : public Node 529c4bf6374SJason M. Bills { 530c4bf6374SJason M. Bills public: 531c4bf6374SJason M. Bills EventLogEntry(CrowApp &app) : 532c4bf6374SJason M. Bills Node(app, 533029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/", 534029573d4SEd Tanous std::string()) 535c4bf6374SJason M. Bills { 536c4bf6374SJason M. Bills entityPrivileges = { 537c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 538c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 539c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 540c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 541c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 542c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 543c4bf6374SJason M. Bills } 544c4bf6374SJason M. Bills 545c4bf6374SJason M. Bills private: 546c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 547c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 548c4bf6374SJason M. Bills { 549c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 550029573d4SEd Tanous if (params.size() != 1) 551c4bf6374SJason M. Bills { 552c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 553c4bf6374SJason M. Bills return; 554c4bf6374SJason M. Bills } 555029573d4SEd Tanous const std::string &entryID = params[0]; 556c4bf6374SJason M. Bills // Convert the unique ID back to a timestamp to find the entry 557c4bf6374SJason M. Bills uint64_t ts = 0; 558c4bf6374SJason M. Bills uint16_t index = 0; 559c4bf6374SJason M. Bills if (!getTimestampFromID(asyncResp->res, entryID, ts, index)) 560c4bf6374SJason M. Bills { 561c4bf6374SJason M. Bills return; 562c4bf6374SJason M. Bills } 563c4bf6374SJason M. Bills 564c4bf6374SJason M. Bills sd_journal *journalTmp = nullptr; 565c4bf6374SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 566c4bf6374SJason M. Bills if (ret < 0) 567c4bf6374SJason M. Bills { 568c4bf6374SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 569c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 570c4bf6374SJason M. Bills return; 571c4bf6374SJason M. Bills } 572c4bf6374SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 573c4bf6374SJason M. Bills journalTmp, sd_journal_close); 574c4bf6374SJason M. Bills journalTmp = nullptr; 575c4bf6374SJason M. Bills // Go to the timestamp in the log and move to the entry at the index 576c4bf6374SJason M. Bills ret = sd_journal_seek_realtime_usec(journal.get(), ts); 577c4bf6374SJason M. Bills for (int i = 0; i <= index; i++) 578c4bf6374SJason M. Bills { 579c4bf6374SJason M. Bills sd_journal_next(journal.get()); 580c4bf6374SJason M. Bills } 581c4bf6374SJason M. Bills // Confirm that the entry ID matches what was requested 582c4bf6374SJason M. Bills std::string idStr; 583c4bf6374SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID) 584c4bf6374SJason M. Bills { 585c4bf6374SJason M. Bills messages::resourceMissingAtURI(asyncResp->res, entryID); 586c4bf6374SJason M. Bills return; 587c4bf6374SJason M. Bills } 588c4bf6374SJason M. Bills 589*cd50aa42SJason M. Bills // only use journal entries that contain a REDFISH_MESSAGE_ID field 590c4bf6374SJason M. Bills boost::string_view messageID; 591c4bf6374SJason M. Bills ret = 592c4bf6374SJason M. Bills getJournalMetadata(journal.get(), "REDFISH_MESSAGE_ID", messageID); 593c4bf6374SJason M. Bills if (ret < 0) 594c4bf6374SJason M. Bills { 595029573d4SEd Tanous messages::resourceNotFound(asyncResp->res, "LogEntry", "system"); 596c4bf6374SJason M. Bills return; 597c4bf6374SJason M. Bills } 598c4bf6374SJason M. Bills 599029573d4SEd Tanous if (fillEventLogEntryJson(entryID, messageID, journal.get(), 600c4bf6374SJason M. Bills asyncResp->res.jsonValue) != 0) 601c4bf6374SJason M. Bills { 602c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 603c4bf6374SJason M. Bills return; 604c4bf6374SJason M. Bills } 605c4bf6374SJason M. Bills } 606c4bf6374SJason M. Bills }; 607c4bf6374SJason M. Bills 608c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node 609c4bf6374SJason M. Bills { 610c4bf6374SJason M. Bills public: 611c4bf6374SJason M. Bills template <typename CrowApp> 612c4bf6374SJason M. Bills BMCLogServiceCollection(CrowApp &app) : 6134ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/") 6141da66f75SEd Tanous { 6151da66f75SEd Tanous entityPrivileges = { 616e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 617e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 618e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 619e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 620e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 621e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 6221da66f75SEd Tanous } 6231da66f75SEd Tanous 6241da66f75SEd Tanous private: 6251da66f75SEd Tanous /** 6261da66f75SEd Tanous * Functions triggers appropriate requests on DBus 6271da66f75SEd Tanous */ 6281da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 6291da66f75SEd Tanous const std::vector<std::string> ¶ms) override 6301da66f75SEd Tanous { 631e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 6321da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 6331da66f75SEd Tanous // it has a duplicate entry for members 634e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 6351da66f75SEd Tanous "#LogServiceCollection.LogServiceCollection"; 636e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 637c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection"; 638e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 639e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices"; 640e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection"; 641e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 6421da66f75SEd Tanous "Collection of LogServices for this Manager"; 643c4bf6374SJason M. Bills nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; 644c4bf6374SJason M. Bills logServiceArray = nlohmann::json::array(); 645c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL 646c4bf6374SJason M. Bills logServiceArray.push_back( 647c4bf6374SJason M. Bills {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}}); 648c4bf6374SJason M. Bills #endif 6491da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG 650c4bf6374SJason M. Bills logServiceArray.push_back( 6514ed77cd5SEd Tanous {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/CpuLog"}}); 6521da66f75SEd Tanous #endif 653e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 654c4bf6374SJason M. Bills logServiceArray.size(); 6551da66f75SEd Tanous } 6561da66f75SEd Tanous }; 6571da66f75SEd Tanous 658c4bf6374SJason M. Bills class BMCJournalLogService : public Node 6591da66f75SEd Tanous { 6601da66f75SEd Tanous public: 6611da66f75SEd Tanous template <typename CrowApp> 662c4bf6374SJason M. Bills BMCJournalLogService(CrowApp &app) : 663c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/") 664e1f26343SJason M. Bills { 665e1f26343SJason M. Bills entityPrivileges = { 666e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 667e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 668e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 669e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 670e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 671e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 672e1f26343SJason M. Bills } 673e1f26343SJason M. Bills 674e1f26343SJason M. Bills private: 675e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 676e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 677e1f26343SJason M. Bills { 678e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 679e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 680e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 6810f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 6820f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal"; 683e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 684e1f26343SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 685c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service"; 686c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service"; 687c4bf6374SJason M. Bills asyncResp->res.jsonValue["Id"] = "BMC Journal"; 688e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 689*cd50aa42SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 690*cd50aa42SJason M. Bills {"@odata.id", 691*cd50aa42SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/"}}; 692e1f26343SJason M. Bills } 693e1f26343SJason M. Bills }; 694e1f26343SJason M. Bills 695c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID, 696e1f26343SJason M. Bills sd_journal *journal, 697c4bf6374SJason M. Bills nlohmann::json &bmcJournalLogEntryJson) 698e1f26343SJason M. Bills { 699e1f26343SJason M. Bills // Get the Log Entry contents 700e1f26343SJason M. Bills int ret = 0; 701e1f26343SJason M. Bills 70216428a1aSJason M. Bills boost::string_view msg; 70316428a1aSJason M. Bills ret = getJournalMetadata(journal, "MESSAGE", msg); 704e1f26343SJason M. Bills if (ret < 0) 705e1f26343SJason M. Bills { 706e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret); 707e1f26343SJason M. Bills return 1; 708e1f26343SJason M. Bills } 709e1f26343SJason M. Bills 710e1f26343SJason M. Bills // Get the severity from the PRIORITY field 711e1f26343SJason M. Bills int severity = 8; // Default to an invalid priority 71216428a1aSJason M. Bills ret = getJournalMetadata(journal, "PRIORITY", 10, severity); 713e1f26343SJason M. Bills if (ret < 0) 714e1f26343SJason M. Bills { 715e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret); 716e1f26343SJason M. Bills return 1; 717e1f26343SJason M. Bills } 718e1f26343SJason M. Bills 719e1f26343SJason M. Bills // Get the Created time from the timestamp 72016428a1aSJason M. Bills std::string entryTimeStr; 72116428a1aSJason M. Bills if (!getEntryTimestamp(journal, entryTimeStr)) 722e1f26343SJason M. Bills { 72316428a1aSJason M. Bills return 1; 724e1f26343SJason M. Bills } 725e1f26343SJason M. Bills 726e1f26343SJason M. Bills // Fill in the log entry with the gathered data 727c4bf6374SJason M. Bills bmcJournalLogEntryJson = { 728e1f26343SJason M. Bills {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 729e1f26343SJason M. Bills {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 730c4bf6374SJason M. Bills {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" + 731c4bf6374SJason M. Bills bmcJournalLogEntryID}, 732e1f26343SJason M. Bills {"Name", "BMC Journal Entry"}, 733c4bf6374SJason M. Bills {"Id", bmcJournalLogEntryID}, 73416428a1aSJason M. Bills {"Message", msg}, 735e1f26343SJason M. Bills {"EntryType", "Oem"}, 736e1f26343SJason M. Bills {"Severity", 737e1f26343SJason M. Bills severity <= 2 ? "Critical" 738e1f26343SJason M. Bills : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""}, 739e1f26343SJason M. Bills {"OemRecordFormat", "Intel BMC Journal Entry"}, 740e1f26343SJason M. Bills {"Created", std::move(entryTimeStr)}}; 741e1f26343SJason M. Bills return 0; 742e1f26343SJason M. Bills } 743e1f26343SJason M. Bills 744c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node 745e1f26343SJason M. Bills { 746e1f26343SJason M. Bills public: 747e1f26343SJason M. Bills template <typename CrowApp> 748c4bf6374SJason M. Bills BMCJournalLogEntryCollection(CrowApp &app) : 749c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/") 750e1f26343SJason M. Bills { 751e1f26343SJason M. Bills entityPrivileges = { 752e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 753e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 754e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 755e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 756e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 757e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 758e1f26343SJason M. Bills } 759e1f26343SJason M. Bills 760e1f26343SJason M. Bills private: 761e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 762e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 763e1f26343SJason M. Bills { 764e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 765193ad2faSJason M. Bills static constexpr const long maxEntriesPerPage = 1000; 766193ad2faSJason M. Bills long skip = 0; 767193ad2faSJason M. Bills long top = maxEntriesPerPage; // Show max entries by default 76816428a1aSJason M. Bills if (!getSkipParam(asyncResp->res, req, skip)) 769193ad2faSJason M. Bills { 770193ad2faSJason M. Bills return; 771193ad2faSJason M. Bills } 77216428a1aSJason M. Bills if (!getTopParam(asyncResp->res, req, top)) 773193ad2faSJason M. Bills { 774193ad2faSJason M. Bills return; 775193ad2faSJason M. Bills } 776e1f26343SJason M. Bills // Collections don't include the static data added by SubRoute because 777e1f26343SJason M. Bills // it has a duplicate entry for members 778e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 779e1f26343SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 7800f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 7810f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; 782e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 783c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection"; 784e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 785c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; 786e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; 787e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 788e1f26343SJason M. Bills "Collection of BMC Journal Entries"; 7890f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 7900f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; 791e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 792e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 793e1f26343SJason M. Bills 794e1f26343SJason M. Bills // Go through the journal and use the timestamp to create a unique ID 795e1f26343SJason M. Bills // for each entry 796e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 797e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 798e1f26343SJason M. Bills if (ret < 0) 799e1f26343SJason M. Bills { 800e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 801f12894f8SJason M. Bills messages::internalError(asyncResp->res); 802e1f26343SJason M. Bills return; 803e1f26343SJason M. Bills } 804e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 805e1f26343SJason M. Bills journalTmp, sd_journal_close); 806e1f26343SJason M. Bills journalTmp = nullptr; 807193ad2faSJason M. Bills uint64_t entryCount = 0; 808e1f26343SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 809e1f26343SJason M. Bills { 810193ad2faSJason M. Bills entryCount++; 811193ad2faSJason M. Bills // Handle paging using skip (number of entries to skip from the 812193ad2faSJason M. Bills // start) and top (number of entries to display) 813193ad2faSJason M. Bills if (entryCount <= skip || entryCount > skip + top) 814193ad2faSJason M. Bills { 815193ad2faSJason M. Bills continue; 816193ad2faSJason M. Bills } 817193ad2faSJason M. Bills 81816428a1aSJason M. Bills std::string idStr; 81916428a1aSJason M. Bills if (!getUniqueEntryID(journal.get(), idStr)) 820e1f26343SJason M. Bills { 821e1f26343SJason M. Bills continue; 822e1f26343SJason M. Bills } 823e1f26343SJason M. Bills 824e1f26343SJason M. Bills logEntryArray.push_back({}); 825c4bf6374SJason M. Bills nlohmann::json &bmcJournalLogEntry = logEntryArray.back(); 826c4bf6374SJason M. Bills if (fillBMCJournalLogEntryJson(idStr, journal.get(), 827c4bf6374SJason M. Bills bmcJournalLogEntry) != 0) 828e1f26343SJason M. Bills { 829f12894f8SJason M. Bills messages::internalError(asyncResp->res); 830e1f26343SJason M. Bills return; 831e1f26343SJason M. Bills } 832e1f26343SJason M. Bills } 833193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 834193ad2faSJason M. Bills if (skip + top < entryCount) 835193ad2faSJason M. Bills { 836193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 837c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" + 838193ad2faSJason M. Bills std::to_string(skip + top); 839193ad2faSJason M. Bills } 840e1f26343SJason M. Bills } 841e1f26343SJason M. Bills }; 842e1f26343SJason M. Bills 843c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node 844e1f26343SJason M. Bills { 845e1f26343SJason M. Bills public: 846c4bf6374SJason M. Bills BMCJournalLogEntry(CrowApp &app) : 847c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/", 848e1f26343SJason M. Bills std::string()) 849e1f26343SJason M. Bills { 850e1f26343SJason M. Bills entityPrivileges = { 851e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 852e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 853e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 854e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 855e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 856e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 857e1f26343SJason M. Bills } 858e1f26343SJason M. Bills 859e1f26343SJason M. Bills private: 860e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 861e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 862e1f26343SJason M. Bills { 863e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 864e1f26343SJason M. Bills if (params.size() != 1) 865e1f26343SJason M. Bills { 866f12894f8SJason M. Bills messages::internalError(asyncResp->res); 867e1f26343SJason M. Bills return; 868e1f26343SJason M. Bills } 86916428a1aSJason M. Bills const std::string &entryID = params[0]; 870e1f26343SJason M. Bills // Convert the unique ID back to a timestamp to find the entry 871e1f26343SJason M. Bills uint64_t ts = 0; 872e1f26343SJason M. Bills uint16_t index = 0; 87316428a1aSJason M. Bills if (!getTimestampFromID(asyncResp->res, entryID, ts, index)) 874e1f26343SJason M. Bills { 87516428a1aSJason M. Bills return; 876e1f26343SJason M. Bills } 877e1f26343SJason M. Bills 878e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 879e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 880e1f26343SJason M. Bills if (ret < 0) 881e1f26343SJason M. Bills { 882e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 883f12894f8SJason M. Bills messages::internalError(asyncResp->res); 884e1f26343SJason M. Bills return; 885e1f26343SJason M. Bills } 886e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 887e1f26343SJason M. Bills journalTmp, sd_journal_close); 888e1f26343SJason M. Bills journalTmp = nullptr; 889e1f26343SJason M. Bills // Go to the timestamp in the log and move to the entry at the index 890e1f26343SJason M. Bills ret = sd_journal_seek_realtime_usec(journal.get(), ts); 891e1f26343SJason M. Bills for (int i = 0; i <= index; i++) 892e1f26343SJason M. Bills { 893e1f26343SJason M. Bills sd_journal_next(journal.get()); 894e1f26343SJason M. Bills } 895c4bf6374SJason M. Bills // Confirm that the entry ID matches what was requested 896c4bf6374SJason M. Bills std::string idStr; 897c4bf6374SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID) 898c4bf6374SJason M. Bills { 899c4bf6374SJason M. Bills messages::resourceMissingAtURI(asyncResp->res, entryID); 900c4bf6374SJason M. Bills return; 901c4bf6374SJason M. Bills } 902c4bf6374SJason M. Bills 903c4bf6374SJason M. Bills if (fillBMCJournalLogEntryJson(entryID, journal.get(), 904e1f26343SJason M. Bills asyncResp->res.jsonValue) != 0) 905e1f26343SJason M. Bills { 906f12894f8SJason M. Bills messages::internalError(asyncResp->res); 907e1f26343SJason M. Bills return; 908e1f26343SJason M. Bills } 909e1f26343SJason M. Bills } 910e1f26343SJason M. Bills }; 911e1f26343SJason M. Bills 912e1f26343SJason M. Bills class CPULogService : public Node 913e1f26343SJason M. Bills { 914e1f26343SJason M. Bills public: 915e1f26343SJason M. Bills template <typename CrowApp> 916e1f26343SJason M. Bills CPULogService(CrowApp &app) : 917e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/") 9181da66f75SEd Tanous { 9191da66f75SEd Tanous entityPrivileges = { 920e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 921e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 922e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 923e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 924e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 925e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 9261da66f75SEd Tanous } 9271da66f75SEd Tanous 9281da66f75SEd Tanous private: 9291da66f75SEd Tanous /** 9301da66f75SEd Tanous * Functions triggers appropriate requests on DBus 9311da66f75SEd Tanous */ 9321da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 9331da66f75SEd Tanous const std::vector<std::string> ¶ms) override 9341da66f75SEd Tanous { 935e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 9361da66f75SEd Tanous // Copy over the static data to include the entries added by SubRoute 9370f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 9380f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog"; 939e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 940e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 941e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 942c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 943e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service"; 944e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = "CPU Log Service"; 945e1f26343SJason M. Bills asyncResp->res.jsonValue["Id"] = "CPU Log"; 946e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 947e1f26343SJason M. Bills asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 948*cd50aa42SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 949*cd50aa42SJason M. Bills {"@odata.id", 950*cd50aa42SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries"}}; 951e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"] = { 9521da66f75SEd Tanous {"Oem", 9531da66f75SEd Tanous {{"#CpuLog.Immediate", 954c4bf6374SJason M. Bills {{"target", "/redfish/v1/Managers/bmc/LogServices/CpuLog/" 955c4bf6374SJason M. Bills "Actions/Oem/CpuLog.Immediate"}}}}}}; 9561da66f75SEd Tanous 9571da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI 958e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"]["Oem"].push_back( 9591da66f75SEd Tanous {"#CpuLog.SendRawPeci", 960c4bf6374SJason M. Bills {{"target", "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/" 961c4bf6374SJason M. Bills "Oem/CpuLog.SendRawPeci"}}}); 9621da66f75SEd Tanous #endif 9631da66f75SEd Tanous } 9641da66f75SEd Tanous }; 9651da66f75SEd Tanous 966e1f26343SJason M. Bills class CPULogEntryCollection : public Node 9671da66f75SEd Tanous { 9681da66f75SEd Tanous public: 9691da66f75SEd Tanous template <typename CrowApp> 970e1f26343SJason M. Bills CPULogEntryCollection(CrowApp &app) : 971e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/") 9721da66f75SEd Tanous { 9731da66f75SEd Tanous entityPrivileges = { 974e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 975e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 976e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 977e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 978e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 979e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 9801da66f75SEd Tanous } 9811da66f75SEd Tanous 9821da66f75SEd Tanous private: 9831da66f75SEd Tanous /** 9841da66f75SEd Tanous * Functions triggers appropriate requests on DBus 9851da66f75SEd Tanous */ 9861da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 9871da66f75SEd Tanous const std::vector<std::string> ¶ms) override 9881da66f75SEd Tanous { 989e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 9901da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 9911da66f75SEd Tanous // it has a duplicate entry for members 992e1f26343SJason M. Bills auto getLogEntriesCallback = [asyncResp]( 993e1f26343SJason M. Bills const boost::system::error_code ec, 9941da66f75SEd Tanous const std::vector<std::string> &resp) { 9951da66f75SEd Tanous if (ec) 9961da66f75SEd Tanous { 9971da66f75SEd Tanous if (ec.value() != 9981da66f75SEd Tanous boost::system::errc::no_such_file_or_directory) 9991da66f75SEd Tanous { 10001da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to get entries ec: " 10011da66f75SEd Tanous << ec.message(); 1002f12894f8SJason M. Bills messages::internalError(asyncResp->res); 10031da66f75SEd Tanous return; 10041da66f75SEd Tanous } 10051da66f75SEd Tanous } 1006e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 10071da66f75SEd Tanous "#LogEntryCollection.LogEntryCollection"; 10080f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 10090f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries"; 1010e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 1011c4bf6374SJason M. Bills "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection"; 1012e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 1013e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries"; 1014e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries"; 1015e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 1016e1f26343SJason M. Bills "Collection of CPU Log Entries"; 1017e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 1018e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 10191da66f75SEd Tanous for (const std::string &objpath : resp) 10201da66f75SEd Tanous { 10211da66f75SEd Tanous // Don't list the immediate log 10224ed77cd5SEd Tanous if (objpath.compare(cpuLogImmediatePath) == 0) 10231da66f75SEd Tanous { 10241da66f75SEd Tanous continue; 10251da66f75SEd Tanous } 10264ed77cd5SEd Tanous std::size_t lastPos = objpath.rfind("/"); 10274ed77cd5SEd Tanous if (lastPos != std::string::npos) 10281da66f75SEd Tanous { 1029e1f26343SJason M. Bills logEntryArray.push_back( 1030e1f26343SJason M. Bills {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/" 1031e1f26343SJason M. Bills "CpuLog/Entries/" + 10324ed77cd5SEd Tanous objpath.substr(lastPos + 1)}}); 10331da66f75SEd Tanous } 10341da66f75SEd Tanous } 1035e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 1036e1f26343SJason M. Bills logEntryArray.size(); 10371da66f75SEd Tanous }; 10381da66f75SEd Tanous crow::connections::systemBus->async_method_call( 10391da66f75SEd Tanous std::move(getLogEntriesCallback), 10401da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", 10411da66f75SEd Tanous "/xyz/openbmc_project/object_mapper", 10421da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0, 10434ed77cd5SEd Tanous std::array<const char *, 1>{cpuLogInterface}); 10441da66f75SEd Tanous } 10451da66f75SEd Tanous }; 10461da66f75SEd Tanous 10471da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog) 10481da66f75SEd Tanous { 1049c4d00437SJason M. Bills nlohmann::json::const_iterator cdIt = cpuLog.find("crashlog_data"); 1050c4d00437SJason M. Bills if (cdIt != cpuLog.end()) 10511da66f75SEd Tanous { 1052c4d00437SJason M. Bills nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO"); 1053c4d00437SJason M. Bills if (siIt != cdIt->end()) 10541da66f75SEd Tanous { 1055c4d00437SJason M. Bills nlohmann::json::const_iterator tsIt = siIt->find("timestamp"); 1056c4d00437SJason M. Bills if (tsIt != siIt->end()) 1057c4d00437SJason M. Bills { 1058c4d00437SJason M. Bills const std::string *logTime = 1059c4d00437SJason M. Bills tsIt->get_ptr<const std::string *>(); 10601da66f75SEd Tanous if (logTime != nullptr) 10611da66f75SEd Tanous { 10621da66f75SEd Tanous return *logTime; 10631da66f75SEd Tanous } 10641da66f75SEd Tanous } 10651da66f75SEd Tanous } 1066c4d00437SJason M. Bills } 10671da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to find log timestamp"; 10681da66f75SEd Tanous 10691da66f75SEd Tanous return std::string(); 10701da66f75SEd Tanous } 10711da66f75SEd Tanous 1072e1f26343SJason M. Bills class CPULogEntry : public Node 10731da66f75SEd Tanous { 10741da66f75SEd Tanous public: 1075e1f26343SJason M. Bills CPULogEntry(CrowApp &app) : 10764ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/", 10771da66f75SEd Tanous std::string()) 10781da66f75SEd Tanous { 10791da66f75SEd Tanous entityPrivileges = { 1080e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1081e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1082e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1083e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1084e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1085e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 10861da66f75SEd Tanous } 10871da66f75SEd Tanous 10881da66f75SEd Tanous private: 10891da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 10901da66f75SEd Tanous const std::vector<std::string> ¶ms) override 10911da66f75SEd Tanous { 1092e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 10931da66f75SEd Tanous if (params.size() != 1) 10941da66f75SEd Tanous { 1095f12894f8SJason M. Bills messages::internalError(asyncResp->res); 10961da66f75SEd Tanous return; 10971da66f75SEd Tanous } 10984ed77cd5SEd Tanous const uint8_t logId = std::atoi(params[0].c_str()); 1099abf2add6SEd Tanous auto getStoredLogCallback = [asyncResp, logId]( 1100abf2add6SEd Tanous const boost::system::error_code ec, 1101abf2add6SEd Tanous const std::variant<std::string> &resp) { 11021da66f75SEd Tanous if (ec) 11031da66f75SEd Tanous { 1104abf2add6SEd Tanous BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message(); 1105f12894f8SJason M. Bills messages::internalError(asyncResp->res); 11061da66f75SEd Tanous return; 11071da66f75SEd Tanous } 1108abf2add6SEd Tanous const std::string *log = std::get_if<std::string>(&resp); 11091da66f75SEd Tanous if (log == nullptr) 11101da66f75SEd Tanous { 1111f12894f8SJason M. Bills messages::internalError(asyncResp->res); 11121da66f75SEd Tanous return; 11131da66f75SEd Tanous } 11141da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 11151da66f75SEd Tanous if (j.is_discarded()) 11161da66f75SEd Tanous { 1117f12894f8SJason M. Bills messages::internalError(asyncResp->res); 11181da66f75SEd Tanous return; 11191da66f75SEd Tanous } 11201da66f75SEd Tanous std::string t = getLogCreatedTime(j); 1121e1f26343SJason M. Bills asyncResp->res.jsonValue = { 11221da66f75SEd Tanous {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 1123abf2add6SEd Tanous {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 11241da66f75SEd Tanous {"@odata.id", 11254ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" + 11264ed77cd5SEd Tanous std::to_string(logId)}, 11271da66f75SEd Tanous {"Name", "CPU Debug Log"}, 11284ed77cd5SEd Tanous {"Id", logId}, 11291da66f75SEd Tanous {"EntryType", "Oem"}, 11301da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 11311da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 11321da66f75SEd Tanous {"Created", std::move(t)}}; 11331da66f75SEd Tanous }; 11341da66f75SEd Tanous crow::connections::systemBus->async_method_call( 11354ed77cd5SEd Tanous std::move(getStoredLogCallback), cpuLogObject, 11364ed77cd5SEd Tanous cpuLogPath + std::string("/") + std::to_string(logId), 11374ed77cd5SEd Tanous "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log"); 11381da66f75SEd Tanous } 11391da66f75SEd Tanous }; 11401da66f75SEd Tanous 1141e1f26343SJason M. Bills class ImmediateCPULog : public Node 11421da66f75SEd Tanous { 11431da66f75SEd Tanous public: 1144e1f26343SJason M. Bills ImmediateCPULog(CrowApp &app) : 11454ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 1146e1f26343SJason M. Bills "CpuLog.Immediate/") 11471da66f75SEd Tanous { 11481da66f75SEd Tanous entityPrivileges = { 1149e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1150e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1151e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1152e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1153e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1154e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 11551da66f75SEd Tanous } 11561da66f75SEd Tanous 11571da66f75SEd Tanous private: 11581da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 11591da66f75SEd Tanous const std::vector<std::string> ¶ms) override 11601da66f75SEd Tanous { 1161e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 11621da66f75SEd Tanous static std::unique_ptr<sdbusplus::bus::match::match> 11631da66f75SEd Tanous immediateLogMatcher; 11641da66f75SEd Tanous 11651da66f75SEd Tanous // Only allow one Immediate Log request at a time 11661da66f75SEd Tanous if (immediateLogMatcher != nullptr) 11671da66f75SEd Tanous { 1168e1f26343SJason M. Bills asyncResp->res.addHeader("Retry-After", "30"); 1169f12894f8SJason M. Bills messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); 11701da66f75SEd Tanous return; 11711da66f75SEd Tanous } 11721da66f75SEd Tanous // Make this static so it survives outside this method 11731da66f75SEd Tanous static boost::asio::deadline_timer timeout(*req.ioService); 11741da66f75SEd Tanous 11751da66f75SEd Tanous timeout.expires_from_now(boost::posix_time::seconds(30)); 1176e1f26343SJason M. Bills timeout.async_wait([asyncResp](const boost::system::error_code &ec) { 11771da66f75SEd Tanous immediateLogMatcher = nullptr; 11781da66f75SEd Tanous if (ec) 11791da66f75SEd Tanous { 11801da66f75SEd Tanous // operation_aborted is expected if timer is canceled before 11811da66f75SEd Tanous // completion. 11821da66f75SEd Tanous if (ec != boost::asio::error::operation_aborted) 11831da66f75SEd Tanous { 11841da66f75SEd Tanous BMCWEB_LOG_ERROR << "Async_wait failed " << ec; 11851da66f75SEd Tanous } 11861da66f75SEd Tanous return; 11871da66f75SEd Tanous } 11881da66f75SEd Tanous BMCWEB_LOG_ERROR << "Timed out waiting for immediate log"; 11891da66f75SEd Tanous 1190f12894f8SJason M. Bills messages::internalError(asyncResp->res); 11911da66f75SEd Tanous }); 11921da66f75SEd Tanous 1193e1f26343SJason M. Bills auto immediateLogMatcherCallback = [asyncResp]( 11941da66f75SEd Tanous sdbusplus::message::message &m) { 11951da66f75SEd Tanous BMCWEB_LOG_DEBUG << "Immediate log available match fired"; 11961da66f75SEd Tanous boost::system::error_code ec; 11971da66f75SEd Tanous timeout.cancel(ec); 11981da66f75SEd Tanous if (ec) 11991da66f75SEd Tanous { 12001da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " << ec; 12011da66f75SEd Tanous } 12024ed77cd5SEd Tanous sdbusplus::message::object_path objPath; 12031da66f75SEd Tanous boost::container::flat_map< 1204abf2add6SEd Tanous std::string, boost::container::flat_map< 1205abf2add6SEd Tanous std::string, std::variant<std::string>>> 12064ed77cd5SEd Tanous interfacesAdded; 12074ed77cd5SEd Tanous m.read(objPath, interfacesAdded); 1208abf2add6SEd Tanous const std::string *log = std::get_if<std::string>( 12091b6b96c5SEd Tanous &interfacesAdded[cpuLogInterface]["Log"]); 12101da66f75SEd Tanous if (log == nullptr) 12111da66f75SEd Tanous { 1212f12894f8SJason M. Bills messages::internalError(asyncResp->res); 12131da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 12141da66f75SEd Tanous // match object inside which this lambda is executing. Once it 12151da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 12161da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 12171da66f75SEd Tanous // be the last thing done. 12181da66f75SEd Tanous immediateLogMatcher = nullptr; 12191da66f75SEd Tanous return; 12201da66f75SEd Tanous } 12211da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 12221da66f75SEd Tanous if (j.is_discarded()) 12231da66f75SEd Tanous { 1224f12894f8SJason M. Bills messages::internalError(asyncResp->res); 12251da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 12261da66f75SEd Tanous // match object inside which this lambda is executing. Once it 12271da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 12281da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 12291da66f75SEd Tanous // be the last thing done. 12301da66f75SEd Tanous immediateLogMatcher = nullptr; 12311da66f75SEd Tanous return; 12321da66f75SEd Tanous } 12331da66f75SEd Tanous std::string t = getLogCreatedTime(j); 1234e1f26343SJason M. Bills asyncResp->res.jsonValue = { 12351da66f75SEd Tanous {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 12361da66f75SEd Tanous {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 12371da66f75SEd Tanous {"Name", "CPU Debug Log"}, 12381da66f75SEd Tanous {"EntryType", "Oem"}, 12391da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 12401da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 12411da66f75SEd Tanous {"Created", std::move(t)}}; 12421da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 12431da66f75SEd Tanous // match object inside which this lambda is executing. Once it is 12441da66f75SEd Tanous // set to nullptr, the match object will be destroyed and the lambda 12451da66f75SEd Tanous // will lose its context, including res, so it needs to be the last 12461da66f75SEd Tanous // thing done. 12471da66f75SEd Tanous immediateLogMatcher = nullptr; 12481da66f75SEd Tanous }; 12491da66f75SEd Tanous immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>( 12501da66f75SEd Tanous *crow::connections::systemBus, 12511da66f75SEd Tanous sdbusplus::bus::match::rules::interfacesAdded() + 12524ed77cd5SEd Tanous sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath), 12531da66f75SEd Tanous std::move(immediateLogMatcherCallback)); 12541da66f75SEd Tanous 12551da66f75SEd Tanous auto generateImmediateLogCallback = 1256e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 12571da66f75SEd Tanous const std::string &resp) { 12581da66f75SEd Tanous if (ec) 12591da66f75SEd Tanous { 12601da66f75SEd Tanous if (ec.value() == 12611da66f75SEd Tanous boost::system::errc::operation_not_supported) 12621da66f75SEd Tanous { 1263f12894f8SJason M. Bills messages::resourceInStandby(asyncResp->res); 12641da66f75SEd Tanous } 12651da66f75SEd Tanous else 12661da66f75SEd Tanous { 1267f12894f8SJason M. Bills messages::internalError(asyncResp->res); 12681da66f75SEd Tanous } 12691da66f75SEd Tanous boost::system::error_code timeoutec; 12701da66f75SEd Tanous timeout.cancel(timeoutec); 12711da66f75SEd Tanous if (timeoutec) 12721da66f75SEd Tanous { 12731da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " 12741da66f75SEd Tanous << timeoutec; 12751da66f75SEd Tanous } 12761da66f75SEd Tanous immediateLogMatcher = nullptr; 12771da66f75SEd Tanous return; 12781da66f75SEd Tanous } 12791da66f75SEd Tanous }; 12801da66f75SEd Tanous crow::connections::systemBus->async_method_call( 12814ed77cd5SEd Tanous std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath, 12824ed77cd5SEd Tanous cpuLogImmediateInterface, "GenerateImmediateLog"); 12831da66f75SEd Tanous } 12841da66f75SEd Tanous }; 12851da66f75SEd Tanous 1286e1f26343SJason M. Bills class SendRawPECI : public Node 12871da66f75SEd Tanous { 12881da66f75SEd Tanous public: 1289e1f26343SJason M. Bills SendRawPECI(CrowApp &app) : 12904ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 1291e1f26343SJason M. Bills "CpuLog.SendRawPeci/") 12921da66f75SEd Tanous { 12931da66f75SEd Tanous entityPrivileges = { 12941da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 12951da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 12961da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 12971da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 12981da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 12991da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 13001da66f75SEd Tanous } 13011da66f75SEd Tanous 13021da66f75SEd Tanous private: 13031da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 13041da66f75SEd Tanous const std::vector<std::string> ¶ms) override 13051da66f75SEd Tanous { 1306e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1307b1556427SEd Tanous uint8_t clientAddress = 0; 1308b1556427SEd Tanous uint8_t readLength = 0; 13091da66f75SEd Tanous std::vector<uint8_t> peciCommand; 1310b1556427SEd Tanous if (!json_util::readJson(req, res, "ClientAddress", clientAddress, 1311b1556427SEd Tanous "ReadLength", readLength, "PECICommand", 1312b1556427SEd Tanous peciCommand)) 13131da66f75SEd Tanous { 13141da66f75SEd Tanous return; 13151da66f75SEd Tanous } 1316b1556427SEd Tanous 13171da66f75SEd Tanous // Callback to return the Raw PECI response 1318e1f26343SJason M. Bills auto sendRawPECICallback = 1319e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 13201da66f75SEd Tanous const std::vector<uint8_t> &resp) { 13211da66f75SEd Tanous if (ec) 13221da66f75SEd Tanous { 13231da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to send PECI command ec: " 13241da66f75SEd Tanous << ec.message(); 1325f12894f8SJason M. Bills messages::internalError(asyncResp->res); 13261da66f75SEd Tanous return; 13271da66f75SEd Tanous } 1328e1f26343SJason M. Bills asyncResp->res.jsonValue = {{"Name", "PECI Command Response"}, 13291da66f75SEd Tanous {"PECIResponse", resp}}; 13301da66f75SEd Tanous }; 13311da66f75SEd Tanous // Call the SendRawPECI command with the provided data 13321da66f75SEd Tanous crow::connections::systemBus->async_method_call( 1333e1f26343SJason M. Bills std::move(sendRawPECICallback), cpuLogObject, cpuLogPath, 1334e1f26343SJason M. Bills cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength, 13354ed77cd5SEd Tanous peciCommand); 13361da66f75SEd Tanous } 13371da66f75SEd Tanous }; 13381da66f75SEd Tanous 13391da66f75SEd Tanous } // namespace redfish 1340