11da66f75SEd Tanous /* 21da66f75SEd Tanous // Copyright (c) 2018 Intel Corporation 31da66f75SEd Tanous // 41da66f75SEd Tanous // Licensed under the Apache License, Version 2.0 (the "License"); 51da66f75SEd Tanous // you may not use this file except in compliance with the License. 61da66f75SEd Tanous // You may obtain a copy of the License at 71da66f75SEd Tanous // 81da66f75SEd Tanous // http://www.apache.org/licenses/LICENSE-2.0 91da66f75SEd Tanous // 101da66f75SEd Tanous // Unless required by applicable law or agreed to in writing, software 111da66f75SEd Tanous // distributed under the License is distributed on an "AS IS" BASIS, 121da66f75SEd Tanous // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131da66f75SEd Tanous // See the License for the specific language governing permissions and 141da66f75SEd Tanous // limitations under the License. 151da66f75SEd Tanous */ 161da66f75SEd Tanous #pragma once 171da66f75SEd Tanous 181da66f75SEd Tanous #include "node.hpp" 191da66f75SEd Tanous 20*e1f26343SJason M. Bills #include <systemd/sd-journal.h> 21*e1f26343SJason M. Bills 221da66f75SEd Tanous #include <boost/container/flat_map.hpp> 23*e1f26343SJason M. Bills #include <boost/utility/string_view.hpp> 241da66f75SEd Tanous #include <experimental/filesystem> 251da66f75SEd Tanous 261da66f75SEd Tanous namespace redfish 271da66f75SEd Tanous { 281da66f75SEd Tanous 294ed77cd5SEd Tanous constexpr char const *cpuLogObject = "com.intel.CpuDebugLog"; 304ed77cd5SEd Tanous constexpr char const *cpuLogPath = "/com/intel/CpuDebugLog"; 314ed77cd5SEd Tanous constexpr char const *cpuLogImmediatePath = "/com/intel/CpuDebugLog/Immediate"; 324ed77cd5SEd Tanous constexpr char const *cpuLogInterface = "com.intel.CpuDebugLog"; 334ed77cd5SEd Tanous constexpr char const *cpuLogImmediateInterface = 341da66f75SEd Tanous "com.intel.CpuDebugLog.Immediate"; 35*e1f26343SJason M. Bills constexpr char const *cpuLogRawPECIInterface = 361da66f75SEd Tanous "com.intel.CpuDebugLog.SendRawPeci"; 371da66f75SEd Tanous 381da66f75SEd Tanous namespace fs = std::experimental::filesystem; 391da66f75SEd Tanous 401da66f75SEd Tanous class LogServiceCollection : public Node 411da66f75SEd Tanous { 421da66f75SEd Tanous public: 431da66f75SEd Tanous template <typename CrowApp> 441da66f75SEd Tanous LogServiceCollection(CrowApp &app) : 454ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/") 461da66f75SEd Tanous { 471da66f75SEd Tanous // Collections use static ID for SubRoute to add to its parent, but only 481da66f75SEd Tanous // load dynamic data so the duplicate static members don't get displayed 494ed77cd5SEd Tanous Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices"; 501da66f75SEd Tanous entityPrivileges = { 51*e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 52*e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 53*e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 54*e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 55*e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 56*e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 571da66f75SEd Tanous } 581da66f75SEd Tanous 591da66f75SEd Tanous private: 601da66f75SEd Tanous /** 611da66f75SEd Tanous * Functions triggers appropriate requests on DBus 621da66f75SEd Tanous */ 631da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 641da66f75SEd Tanous const std::vector<std::string> ¶ms) override 651da66f75SEd Tanous { 66*e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 671da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 681da66f75SEd Tanous // it has a duplicate entry for members 69*e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 701da66f75SEd Tanous "#LogServiceCollection.LogServiceCollection"; 71*e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 721da66f75SEd Tanous "/redfish/v1/" 731da66f75SEd Tanous "$metadata#LogServiceCollection.LogServiceCollection"; 74*e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 75*e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices"; 76*e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection"; 77*e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 781da66f75SEd Tanous "Collection of LogServices for this Manager"; 79*e1f26343SJason M. Bills nlohmann::json &logserviceArray = asyncResp->res.jsonValue["Members"]; 801da66f75SEd Tanous logserviceArray = nlohmann::json::array(); 81*e1f26343SJason M. Bills logserviceArray.push_back( 82*e1f26343SJason M. Bills {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog"}}); 831da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG 841da66f75SEd Tanous logserviceArray.push_back( 854ed77cd5SEd Tanous {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/CpuLog"}}); 861da66f75SEd Tanous #endif 87*e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 88*e1f26343SJason M. Bills logserviceArray.size(); 891da66f75SEd Tanous } 901da66f75SEd Tanous }; 911da66f75SEd Tanous 92*e1f26343SJason M. Bills class BMCLogService : public Node 931da66f75SEd Tanous { 941da66f75SEd Tanous public: 951da66f75SEd Tanous template <typename CrowApp> 96*e1f26343SJason M. Bills BMCLogService(CrowApp &app) : 97*e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/") 98*e1f26343SJason M. Bills { 99*e1f26343SJason M. Bills // Set the id for SubRoute 100*e1f26343SJason M. Bills Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/BmcLog"; 101*e1f26343SJason M. Bills entityPrivileges = { 102*e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 103*e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 104*e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 105*e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 106*e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 107*e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 108*e1f26343SJason M. Bills } 109*e1f26343SJason M. Bills 110*e1f26343SJason M. Bills private: 111*e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 112*e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 113*e1f26343SJason M. Bills { 114*e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 115*e1f26343SJason M. Bills // Copy over the static data to include the entries added by SubRoute 116*e1f26343SJason M. Bills asyncResp->res.jsonValue = Node::json; 117*e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 118*e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 119*e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 120*e1f26343SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 121*e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Log Service"; 122*e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = "BMC Log Service"; 123*e1f26343SJason M. Bills asyncResp->res.jsonValue["Id"] = "BMC Log"; 124*e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 125*e1f26343SJason M. Bills } 126*e1f26343SJason M. Bills }; 127*e1f26343SJason M. Bills 128*e1f26343SJason M. Bills static int fillBMCLogEntryJson(const std::string &bmcLogEntryID, 129*e1f26343SJason M. Bills sd_journal *journal, 130*e1f26343SJason M. Bills nlohmann::json &bmcLogEntryJson) 131*e1f26343SJason M. Bills { 132*e1f26343SJason M. Bills // Get the Log Entry contents 133*e1f26343SJason M. Bills int ret = 0; 134*e1f26343SJason M. Bills const char *data = nullptr; 135*e1f26343SJason M. Bills size_t length = 0; 136*e1f26343SJason M. Bills 137*e1f26343SJason M. Bills ret = 138*e1f26343SJason M. Bills sd_journal_get_data(journal, "MESSAGE", (const void **)&data, &length); 139*e1f26343SJason M. Bills if (ret < 0) 140*e1f26343SJason M. Bills { 141*e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret); 142*e1f26343SJason M. Bills return 1; 143*e1f26343SJason M. Bills } 144*e1f26343SJason M. Bills boost::string_view msg; 145*e1f26343SJason M. Bills msg = boost::string_view(data, length); 146*e1f26343SJason M. Bills // Only use the content after the "=" character. 147*e1f26343SJason M. Bills msg.remove_prefix(std::min(msg.find("=") + 1, msg.size())); 148*e1f26343SJason M. Bills 149*e1f26343SJason M. Bills // Get the severity from the PRIORITY field 150*e1f26343SJason M. Bills boost::string_view priority; 151*e1f26343SJason M. Bills int severity = 8; // Default to an invalid priority 152*e1f26343SJason M. Bills ret = 153*e1f26343SJason M. Bills sd_journal_get_data(journal, "PRIORITY", (const void **)&data, &length); 154*e1f26343SJason M. Bills if (ret < 0) 155*e1f26343SJason M. Bills { 156*e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret); 157*e1f26343SJason M. Bills return 1; 158*e1f26343SJason M. Bills } 159*e1f26343SJason M. Bills priority = boost::string_view(data, length); 160*e1f26343SJason M. Bills // Check length for sanity. Must be a single digit in the form 161*e1f26343SJason M. Bills // "PRIORITY=[0-7]" 162*e1f26343SJason M. Bills if (priority.size() > sizeof("PRIORITY=0")) 163*e1f26343SJason M. Bills { 164*e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Invalid PRIORITY field length"; 165*e1f26343SJason M. Bills return 1; 166*e1f26343SJason M. Bills } 167*e1f26343SJason M. Bills // Only use the content after the "=" character. 168*e1f26343SJason M. Bills priority.remove_prefix(std::min(priority.find("=") + 1, priority.size())); 169*e1f26343SJason M. Bills severity = strtol(priority.data(), nullptr, 10); 170*e1f26343SJason M. Bills 171*e1f26343SJason M. Bills // Get the Created time from the timestamp 172*e1f26343SJason M. Bills // Get the entry timestamp 173*e1f26343SJason M. Bills uint64_t timestamp = 0; 174*e1f26343SJason M. Bills ret = sd_journal_get_realtime_usec(journal, ×tamp); 175*e1f26343SJason M. Bills if (ret < 0) 176*e1f26343SJason M. Bills { 177*e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 178*e1f26343SJason M. Bills << strerror(-ret); 179*e1f26343SJason M. Bills } 180*e1f26343SJason M. Bills time_t t = 181*e1f26343SJason M. Bills static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s 182*e1f26343SJason M. Bills struct tm *loctime = localtime(&t); 183*e1f26343SJason M. Bills char entryTime[64] = {}; 184*e1f26343SJason M. Bills if (NULL != loctime) 185*e1f26343SJason M. Bills { 186*e1f26343SJason M. Bills strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime); 187*e1f26343SJason M. Bills } 188*e1f26343SJason M. Bills // Insert the ':' into the timezone 189*e1f26343SJason M. Bills boost::string_view t1(entryTime); 190*e1f26343SJason M. Bills boost::string_view t2(entryTime); 191*e1f26343SJason M. Bills if (t1.size() > 2 && t2.size() > 2) 192*e1f26343SJason M. Bills { 193*e1f26343SJason M. Bills t1.remove_suffix(2); 194*e1f26343SJason M. Bills t2.remove_prefix(t2.size() - 2); 195*e1f26343SJason M. Bills } 196*e1f26343SJason M. Bills const std::string entryTimeStr(t1.to_string() + ":" + t2.to_string()); 197*e1f26343SJason M. Bills 198*e1f26343SJason M. Bills // Fill in the log entry with the gathered data 199*e1f26343SJason M. Bills bmcLogEntryJson = { 200*e1f26343SJason M. Bills {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 201*e1f26343SJason M. Bills {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 202*e1f26343SJason M. Bills {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/" + 203*e1f26343SJason M. Bills bmcLogEntryID}, 204*e1f26343SJason M. Bills {"Name", "BMC Journal Entry"}, 205*e1f26343SJason M. Bills {"Id", bmcLogEntryID}, 206*e1f26343SJason M. Bills {"Message", msg.to_string()}, 207*e1f26343SJason M. Bills {"EntryType", "Oem"}, 208*e1f26343SJason M. Bills {"Severity", 209*e1f26343SJason M. Bills severity <= 2 ? "Critical" 210*e1f26343SJason M. Bills : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""}, 211*e1f26343SJason M. Bills {"OemRecordFormat", "Intel BMC Journal Entry"}, 212*e1f26343SJason M. Bills {"Created", std::move(entryTimeStr)}}; 213*e1f26343SJason M. Bills return 0; 214*e1f26343SJason M. Bills } 215*e1f26343SJason M. Bills 216*e1f26343SJason M. Bills class BMCLogEntryCollection : public Node 217*e1f26343SJason M. Bills { 218*e1f26343SJason M. Bills public: 219*e1f26343SJason M. Bills template <typename CrowApp> 220*e1f26343SJason M. Bills BMCLogEntryCollection(CrowApp &app) : 221*e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/") 222*e1f26343SJason M. Bills { 223*e1f26343SJason M. Bills // Collections use static ID for SubRoute to add to its parent, but only 224*e1f26343SJason M. Bills // load dynamic data so the duplicate static members don't get displayed 225*e1f26343SJason M. Bills Node::json["@odata.id"] = 226*e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; 227*e1f26343SJason M. Bills entityPrivileges = { 228*e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 229*e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 230*e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 231*e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 232*e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 233*e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 234*e1f26343SJason M. Bills } 235*e1f26343SJason M. Bills 236*e1f26343SJason M. Bills private: 237*e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 238*e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 239*e1f26343SJason M. Bills { 240*e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 241*e1f26343SJason M. Bills // Collections don't include the static data added by SubRoute because 242*e1f26343SJason M. Bills // it has a duplicate entry for members 243*e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 244*e1f26343SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 245*e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 246*e1f26343SJason M. Bills "/redfish/v1/" 247*e1f26343SJason M. Bills "$metadata#LogEntryCollection.LogEntryCollection"; 248*e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 249*e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; 250*e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; 251*e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 252*e1f26343SJason M. Bills "Collection of BMC Journal Entries"; 253*e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 254*e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 255*e1f26343SJason M. Bills 256*e1f26343SJason M. Bills // Go through the journal and use the timestamp to create a unique ID 257*e1f26343SJason M. Bills // for each entry 258*e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 259*e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 260*e1f26343SJason M. Bills if (ret < 0) 261*e1f26343SJason M. Bills { 262*e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 263*e1f26343SJason M. Bills asyncResp->res.result( 264*e1f26343SJason M. Bills boost::beast::http::status::internal_server_error); 265*e1f26343SJason M. Bills return; 266*e1f26343SJason M. Bills } 267*e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 268*e1f26343SJason M. Bills journalTmp, sd_journal_close); 269*e1f26343SJason M. Bills journalTmp = nullptr; 270*e1f26343SJason M. Bills uint64_t prevTs = 0; 271*e1f26343SJason M. Bills int index = 0; 272*e1f26343SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 273*e1f26343SJason M. Bills { 274*e1f26343SJason M. Bills // Get the entry timestamp 275*e1f26343SJason M. Bills uint64_t curTs = 0; 276*e1f26343SJason M. Bills ret = sd_journal_get_realtime_usec(journal.get(), &curTs); 277*e1f26343SJason M. Bills if (ret < 0) 278*e1f26343SJason M. Bills { 279*e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 280*e1f26343SJason M. Bills << strerror(-ret); 281*e1f26343SJason M. Bills continue; 282*e1f26343SJason M. Bills } 283*e1f26343SJason M. Bills // If the timestamp isn't unique, increment the index 284*e1f26343SJason M. Bills if (curTs == prevTs) 285*e1f26343SJason M. Bills { 286*e1f26343SJason M. Bills index++; 287*e1f26343SJason M. Bills } 288*e1f26343SJason M. Bills else 289*e1f26343SJason M. Bills { 290*e1f26343SJason M. Bills // Otherwise, reset it 291*e1f26343SJason M. Bills index = 0; 292*e1f26343SJason M. Bills } 293*e1f26343SJason M. Bills // Save the timestamp 294*e1f26343SJason M. Bills prevTs = curTs; 295*e1f26343SJason M. Bills 296*e1f26343SJason M. Bills std::string idStr(std::to_string(curTs)); 297*e1f26343SJason M. Bills if (index > 0) 298*e1f26343SJason M. Bills { 299*e1f26343SJason M. Bills idStr += "_" + std::to_string(index); 300*e1f26343SJason M. Bills } 301*e1f26343SJason M. Bills logEntryArray.push_back({}); 302*e1f26343SJason M. Bills nlohmann::json &bmcLogEntry = logEntryArray.back(); 303*e1f26343SJason M. Bills if (fillBMCLogEntryJson(idStr, journal.get(), bmcLogEntry) != 0) 304*e1f26343SJason M. Bills { 305*e1f26343SJason M. Bills asyncResp->res.result( 306*e1f26343SJason M. Bills boost::beast::http::status::internal_server_error); 307*e1f26343SJason M. Bills return; 308*e1f26343SJason M. Bills } 309*e1f26343SJason M. Bills } 310*e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = logEntryArray.size(); 311*e1f26343SJason M. Bills } 312*e1f26343SJason M. Bills }; 313*e1f26343SJason M. Bills 314*e1f26343SJason M. Bills class BMCLogEntry : public Node 315*e1f26343SJason M. Bills { 316*e1f26343SJason M. Bills public: 317*e1f26343SJason M. Bills BMCLogEntry(CrowApp &app) : 318*e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/<str>/", 319*e1f26343SJason M. Bills std::string()) 320*e1f26343SJason M. Bills { 321*e1f26343SJason M. Bills entityPrivileges = { 322*e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 323*e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 324*e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 325*e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 326*e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 327*e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 328*e1f26343SJason M. Bills } 329*e1f26343SJason M. Bills 330*e1f26343SJason M. Bills private: 331*e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 332*e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 333*e1f26343SJason M. Bills { 334*e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 335*e1f26343SJason M. Bills if (params.size() != 1) 336*e1f26343SJason M. Bills { 337*e1f26343SJason M. Bills asyncResp->res.result( 338*e1f26343SJason M. Bills boost::beast::http::status::internal_server_error); 339*e1f26343SJason M. Bills return; 340*e1f26343SJason M. Bills } 341*e1f26343SJason M. Bills // Convert the unique ID back to a timestamp to find the entry 342*e1f26343SJason M. Bills boost::string_view tsStr(params[0]); 343*e1f26343SJason M. Bills boost::string_view indexStr(params[0]); 344*e1f26343SJason M. Bills uint64_t ts = 0; 345*e1f26343SJason M. Bills uint16_t index = 0; 346*e1f26343SJason M. Bills auto underscorePos = tsStr.find("_"); 347*e1f26343SJason M. Bills if (underscorePos == tsStr.npos) 348*e1f26343SJason M. Bills { 349*e1f26343SJason M. Bills // Timestamp has no index 350*e1f26343SJason M. Bills ts = strtoull(tsStr.data(), nullptr, 10); 351*e1f26343SJason M. Bills } 352*e1f26343SJason M. Bills else 353*e1f26343SJason M. Bills { 354*e1f26343SJason M. Bills // Timestamp has an index 355*e1f26343SJason M. Bills tsStr.remove_suffix(tsStr.size() - underscorePos + 1); 356*e1f26343SJason M. Bills ts = strtoull(tsStr.data(), nullptr, 10); 357*e1f26343SJason M. Bills indexStr.remove_prefix(underscorePos + 1); 358*e1f26343SJason M. Bills index = 359*e1f26343SJason M. Bills static_cast<uint16_t>(strtoul(indexStr.data(), nullptr, 10)); 360*e1f26343SJason M. Bills } 361*e1f26343SJason M. Bills 362*e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 363*e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 364*e1f26343SJason M. Bills if (ret < 0) 365*e1f26343SJason M. Bills { 366*e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 367*e1f26343SJason M. Bills asyncResp->res.result( 368*e1f26343SJason M. Bills boost::beast::http::status::internal_server_error); 369*e1f26343SJason M. Bills return; 370*e1f26343SJason M. Bills } 371*e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 372*e1f26343SJason M. Bills journalTmp, sd_journal_close); 373*e1f26343SJason M. Bills journalTmp = nullptr; 374*e1f26343SJason M. Bills // Go to the timestamp in the log and move to the entry at the index 375*e1f26343SJason M. Bills ret = sd_journal_seek_realtime_usec(journal.get(), ts); 376*e1f26343SJason M. Bills for (int i = 0; i <= index; i++) 377*e1f26343SJason M. Bills { 378*e1f26343SJason M. Bills sd_journal_next(journal.get()); 379*e1f26343SJason M. Bills } 380*e1f26343SJason M. Bills if (fillBMCLogEntryJson(params[0], journal.get(), 381*e1f26343SJason M. Bills asyncResp->res.jsonValue) != 0) 382*e1f26343SJason M. Bills { 383*e1f26343SJason M. Bills asyncResp->res.result( 384*e1f26343SJason M. Bills boost::beast::http::status::internal_server_error); 385*e1f26343SJason M. Bills return; 386*e1f26343SJason M. Bills } 387*e1f26343SJason M. Bills } 388*e1f26343SJason M. Bills }; 389*e1f26343SJason M. Bills 390*e1f26343SJason M. Bills class CPULogService : public Node 391*e1f26343SJason M. Bills { 392*e1f26343SJason M. Bills public: 393*e1f26343SJason M. Bills template <typename CrowApp> 394*e1f26343SJason M. Bills CPULogService(CrowApp &app) : 395*e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/") 3961da66f75SEd Tanous { 3971da66f75SEd Tanous // Set the id for SubRoute 3984ed77cd5SEd Tanous Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/CpuLog"; 3991da66f75SEd Tanous entityPrivileges = { 400*e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 401*e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 402*e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 403*e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 404*e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 405*e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 4061da66f75SEd Tanous } 4071da66f75SEd Tanous 4081da66f75SEd Tanous private: 4091da66f75SEd Tanous /** 4101da66f75SEd Tanous * Functions triggers appropriate requests on DBus 4111da66f75SEd Tanous */ 4121da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 4131da66f75SEd Tanous const std::vector<std::string> ¶ms) override 4141da66f75SEd Tanous { 415*e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 4161da66f75SEd Tanous // Copy over the static data to include the entries added by SubRoute 417*e1f26343SJason M. Bills asyncResp->res.jsonValue = Node::json; 418*e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 419*e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 420*e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 421*e1f26343SJason M. Bills "/redfish/v1/" 4221da66f75SEd Tanous "$metadata#LogService.LogService"; 423*e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service"; 424*e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = "CPU Log Service"; 425*e1f26343SJason M. Bills asyncResp->res.jsonValue["Id"] = "CPU Log"; 426*e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 427*e1f26343SJason M. Bills asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 428*e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"] = { 4291da66f75SEd Tanous {"Oem", 4301da66f75SEd Tanous {{"#CpuLog.Immediate", 4311da66f75SEd Tanous {{"target", 4324ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 4331da66f75SEd Tanous "CpuLog.Immediate"}}}}}}; 4341da66f75SEd Tanous 4351da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI 436*e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"]["Oem"].push_back( 4371da66f75SEd Tanous {"#CpuLog.SendRawPeci", 4381da66f75SEd Tanous {{"target", 4394ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 4401da66f75SEd Tanous "CpuLog.SendRawPeci"}}}); 4411da66f75SEd Tanous #endif 4421da66f75SEd Tanous } 4431da66f75SEd Tanous }; 4441da66f75SEd Tanous 445*e1f26343SJason M. Bills class CPULogEntryCollection : public Node 4461da66f75SEd Tanous { 4471da66f75SEd Tanous public: 4481da66f75SEd Tanous template <typename CrowApp> 449*e1f26343SJason M. Bills CPULogEntryCollection(CrowApp &app) : 450*e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/") 4511da66f75SEd Tanous { 4521da66f75SEd Tanous // Collections use static ID for SubRoute to add to its parent, but only 4531da66f75SEd Tanous // load dynamic data so the duplicate static members don't get displayed 4541da66f75SEd Tanous Node::json["@odata.id"] = 4554ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries"; 4561da66f75SEd Tanous entityPrivileges = { 457*e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 458*e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 459*e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 460*e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 461*e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 462*e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 4631da66f75SEd Tanous } 4641da66f75SEd Tanous 4651da66f75SEd Tanous private: 4661da66f75SEd Tanous /** 4671da66f75SEd Tanous * Functions triggers appropriate requests on DBus 4681da66f75SEd Tanous */ 4691da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 4701da66f75SEd Tanous const std::vector<std::string> ¶ms) override 4711da66f75SEd Tanous { 472*e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 4731da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 4741da66f75SEd Tanous // it has a duplicate entry for members 475*e1f26343SJason M. Bills auto getLogEntriesCallback = [asyncResp]( 476*e1f26343SJason M. Bills const boost::system::error_code ec, 4771da66f75SEd Tanous const std::vector<std::string> &resp) { 4781da66f75SEd Tanous if (ec) 4791da66f75SEd Tanous { 4801da66f75SEd Tanous if (ec.value() != 4811da66f75SEd Tanous boost::system::errc::no_such_file_or_directory) 4821da66f75SEd Tanous { 4831da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to get entries ec: " 4841da66f75SEd Tanous << ec.message(); 485*e1f26343SJason M. Bills asyncResp->res.result( 4861da66f75SEd Tanous boost::beast::http::status::internal_server_error); 4871da66f75SEd Tanous return; 4881da66f75SEd Tanous } 4891da66f75SEd Tanous } 490*e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 4911da66f75SEd Tanous "#LogEntryCollection.LogEntryCollection"; 492*e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 4931da66f75SEd Tanous "/redfish/v1/" 4941da66f75SEd Tanous "$metadata#LogEntryCollection.LogEntryCollection"; 495*e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 496*e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries"; 497*e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries"; 498*e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 499*e1f26343SJason M. Bills "Collection of CPU Log Entries"; 500*e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 501*e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 5021da66f75SEd Tanous for (const std::string &objpath : resp) 5031da66f75SEd Tanous { 5041da66f75SEd Tanous // Don't list the immediate log 5054ed77cd5SEd Tanous if (objpath.compare(cpuLogImmediatePath) == 0) 5061da66f75SEd Tanous { 5071da66f75SEd Tanous continue; 5081da66f75SEd Tanous } 5094ed77cd5SEd Tanous std::size_t lastPos = objpath.rfind("/"); 5104ed77cd5SEd Tanous if (lastPos != std::string::npos) 5111da66f75SEd Tanous { 512*e1f26343SJason M. Bills logEntryArray.push_back( 513*e1f26343SJason M. Bills {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/" 514*e1f26343SJason M. Bills "CpuLog/Entries/" + 5154ed77cd5SEd Tanous objpath.substr(lastPos + 1)}}); 5161da66f75SEd Tanous } 5171da66f75SEd Tanous } 518*e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 519*e1f26343SJason M. Bills logEntryArray.size(); 5201da66f75SEd Tanous }; 5211da66f75SEd Tanous crow::connections::systemBus->async_method_call( 5221da66f75SEd Tanous std::move(getLogEntriesCallback), 5231da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", 5241da66f75SEd Tanous "/xyz/openbmc_project/object_mapper", 5251da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0, 5264ed77cd5SEd Tanous std::array<const char *, 1>{cpuLogInterface}); 5271da66f75SEd Tanous } 5281da66f75SEd Tanous }; 5291da66f75SEd Tanous 5301da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog) 5311da66f75SEd Tanous { 5321da66f75SEd Tanous nlohmann::json::const_iterator metaIt = cpuLog.find("metadata"); 5331da66f75SEd Tanous if (metaIt != cpuLog.end()) 5341da66f75SEd Tanous { 5351da66f75SEd Tanous nlohmann::json::const_iterator tsIt = metaIt->find("timestamp"); 5361da66f75SEd Tanous if (tsIt != metaIt->end()) 5371da66f75SEd Tanous { 5381da66f75SEd Tanous const std::string *logTime = tsIt->get_ptr<const std::string *>(); 5391da66f75SEd Tanous if (logTime != nullptr) 5401da66f75SEd Tanous { 5411da66f75SEd Tanous return *logTime; 5421da66f75SEd Tanous } 5431da66f75SEd Tanous } 5441da66f75SEd Tanous } 5451da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to find log timestamp"; 5461da66f75SEd Tanous 5471da66f75SEd Tanous return std::string(); 5481da66f75SEd Tanous } 5491da66f75SEd Tanous 550*e1f26343SJason M. Bills class CPULogEntry : public Node 5511da66f75SEd Tanous { 5521da66f75SEd Tanous public: 553*e1f26343SJason M. Bills CPULogEntry(CrowApp &app) : 5544ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/", 5551da66f75SEd Tanous std::string()) 5561da66f75SEd Tanous { 5571da66f75SEd Tanous entityPrivileges = { 558*e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 559*e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 560*e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 561*e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 562*e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 563*e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 5641da66f75SEd Tanous } 5651da66f75SEd Tanous 5661da66f75SEd Tanous private: 5671da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 5681da66f75SEd Tanous const std::vector<std::string> ¶ms) override 5691da66f75SEd Tanous { 570*e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 5711da66f75SEd Tanous if (params.size() != 1) 5721da66f75SEd Tanous { 573*e1f26343SJason M. Bills asyncResp->res.result( 574*e1f26343SJason M. Bills boost::beast::http::status::internal_server_error); 5751da66f75SEd Tanous return; 5761da66f75SEd Tanous } 5774ed77cd5SEd Tanous const uint8_t logId = std::atoi(params[0].c_str()); 578*e1f26343SJason M. Bills auto getStoredLogCallback = 579*e1f26343SJason M. Bills [asyncResp, 5804ed77cd5SEd Tanous logId](const boost::system::error_code ec, 581*e1f26343SJason M. Bills const sdbusplus::message::variant<std::string> &resp) { 5821da66f75SEd Tanous if (ec) 5831da66f75SEd Tanous { 584*e1f26343SJason M. Bills BMCWEB_LOG_DEBUG << "failed to get log ec: " 585*e1f26343SJason M. Bills << ec.message(); 586*e1f26343SJason M. Bills asyncResp->res.result( 587*e1f26343SJason M. Bills boost::beast::http::status::internal_server_error); 5881da66f75SEd Tanous return; 5891da66f75SEd Tanous } 590*e1f26343SJason M. Bills const std::string *log = 591*e1f26343SJason M. Bills mapbox::getPtr<const std::string>(resp); 5921da66f75SEd Tanous if (log == nullptr) 5931da66f75SEd Tanous { 594*e1f26343SJason M. Bills asyncResp->res.result( 595*e1f26343SJason M. Bills boost::beast::http::status::internal_server_error); 5961da66f75SEd Tanous return; 5971da66f75SEd Tanous } 5981da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 5991da66f75SEd Tanous if (j.is_discarded()) 6001da66f75SEd Tanous { 601*e1f26343SJason M. Bills asyncResp->res.result( 602*e1f26343SJason M. Bills boost::beast::http::status::internal_server_error); 6031da66f75SEd Tanous return; 6041da66f75SEd Tanous } 6051da66f75SEd Tanous std::string t = getLogCreatedTime(j); 606*e1f26343SJason M. Bills asyncResp->res.jsonValue = { 6071da66f75SEd Tanous {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 608*e1f26343SJason M. Bills {"@odata.context", 609*e1f26343SJason M. Bills "/redfish/v1/$metadata#LogEntry.LogEntry"}, 6101da66f75SEd Tanous {"@odata.id", 6114ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" + 6124ed77cd5SEd Tanous std::to_string(logId)}, 6131da66f75SEd Tanous {"Name", "CPU Debug Log"}, 6144ed77cd5SEd Tanous {"Id", logId}, 6151da66f75SEd Tanous {"EntryType", "Oem"}, 6161da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 6171da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 6181da66f75SEd Tanous {"Created", std::move(t)}}; 6191da66f75SEd Tanous }; 6201da66f75SEd Tanous crow::connections::systemBus->async_method_call( 6214ed77cd5SEd Tanous std::move(getStoredLogCallback), cpuLogObject, 6224ed77cd5SEd Tanous cpuLogPath + std::string("/") + std::to_string(logId), 6234ed77cd5SEd Tanous "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log"); 6241da66f75SEd Tanous } 6251da66f75SEd Tanous }; 6261da66f75SEd Tanous 627*e1f26343SJason M. Bills class ImmediateCPULog : public Node 6281da66f75SEd Tanous { 6291da66f75SEd Tanous public: 630*e1f26343SJason M. Bills ImmediateCPULog(CrowApp &app) : 6314ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 632*e1f26343SJason M. Bills "CpuLog.Immediate/") 6331da66f75SEd Tanous { 6341da66f75SEd Tanous entityPrivileges = { 635*e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 636*e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 637*e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 638*e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 639*e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 640*e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 6411da66f75SEd Tanous } 6421da66f75SEd Tanous 6431da66f75SEd Tanous private: 6441da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 6451da66f75SEd Tanous const std::vector<std::string> ¶ms) override 6461da66f75SEd Tanous { 647*e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 6481da66f75SEd Tanous static std::unique_ptr<sdbusplus::bus::match::match> 6491da66f75SEd Tanous immediateLogMatcher; 6501da66f75SEd Tanous 6511da66f75SEd Tanous // Only allow one Immediate Log request at a time 6521da66f75SEd Tanous if (immediateLogMatcher != nullptr) 6531da66f75SEd Tanous { 654*e1f26343SJason M. Bills asyncResp->res.addHeader("Retry-After", "30"); 655*e1f26343SJason M. Bills asyncResp->res.result( 656*e1f26343SJason M. Bills boost::beast::http::status::service_unavailable); 6571da66f75SEd Tanous messages::addMessageToJson( 658*e1f26343SJason M. Bills asyncResp->res.jsonValue, 659*e1f26343SJason M. Bills messages::serviceTemporarilyUnavailable("30"), 6601da66f75SEd Tanous "/CpuLog.Immediate"); 6611da66f75SEd Tanous return; 6621da66f75SEd Tanous } 6631da66f75SEd Tanous // Make this static so it survives outside this method 6641da66f75SEd Tanous static boost::asio::deadline_timer timeout(*req.ioService); 6651da66f75SEd Tanous 6661da66f75SEd Tanous timeout.expires_from_now(boost::posix_time::seconds(30)); 667*e1f26343SJason M. Bills timeout.async_wait([asyncResp](const boost::system::error_code &ec) { 6681da66f75SEd Tanous immediateLogMatcher = nullptr; 6691da66f75SEd Tanous if (ec) 6701da66f75SEd Tanous { 6711da66f75SEd Tanous // operation_aborted is expected if timer is canceled before 6721da66f75SEd Tanous // completion. 6731da66f75SEd Tanous if (ec != boost::asio::error::operation_aborted) 6741da66f75SEd Tanous { 6751da66f75SEd Tanous BMCWEB_LOG_ERROR << "Async_wait failed " << ec; 6761da66f75SEd Tanous } 6771da66f75SEd Tanous return; 6781da66f75SEd Tanous } 6791da66f75SEd Tanous BMCWEB_LOG_ERROR << "Timed out waiting for immediate log"; 6801da66f75SEd Tanous 681*e1f26343SJason M. Bills asyncResp->res.result( 682*e1f26343SJason M. Bills boost::beast::http::status::internal_server_error); 6831da66f75SEd Tanous }); 6841da66f75SEd Tanous 685*e1f26343SJason M. Bills auto immediateLogMatcherCallback = [asyncResp]( 6861da66f75SEd Tanous sdbusplus::message::message &m) { 6871da66f75SEd Tanous BMCWEB_LOG_DEBUG << "Immediate log available match fired"; 6881da66f75SEd Tanous boost::system::error_code ec; 6891da66f75SEd Tanous timeout.cancel(ec); 6901da66f75SEd Tanous if (ec) 6911da66f75SEd Tanous { 6921da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " << ec; 6931da66f75SEd Tanous } 6944ed77cd5SEd Tanous sdbusplus::message::object_path objPath; 6951da66f75SEd Tanous boost::container::flat_map< 6961da66f75SEd Tanous std::string, 6971da66f75SEd Tanous boost::container::flat_map< 6981da66f75SEd Tanous std::string, sdbusplus::message::variant<std::string>>> 6994ed77cd5SEd Tanous interfacesAdded; 7004ed77cd5SEd Tanous m.read(objPath, interfacesAdded); 7011da66f75SEd Tanous const std::string *log = mapbox::getPtr<const std::string>( 7024ed77cd5SEd Tanous interfacesAdded[cpuLogInterface]["Log"]); 7031da66f75SEd Tanous if (log == nullptr) 7041da66f75SEd Tanous { 705*e1f26343SJason M. Bills asyncResp->res.result( 706*e1f26343SJason M. Bills boost::beast::http::status::internal_server_error); 7071da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 7081da66f75SEd Tanous // match object inside which this lambda is executing. Once it 7091da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 7101da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 7111da66f75SEd Tanous // be the last thing done. 7121da66f75SEd Tanous immediateLogMatcher = nullptr; 7131da66f75SEd Tanous return; 7141da66f75SEd Tanous } 7151da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 7161da66f75SEd Tanous if (j.is_discarded()) 7171da66f75SEd Tanous { 718*e1f26343SJason M. Bills asyncResp->res.result( 719*e1f26343SJason M. Bills boost::beast::http::status::internal_server_error); 7201da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 7211da66f75SEd Tanous // match object inside which this lambda is executing. Once it 7221da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 7231da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 7241da66f75SEd Tanous // be the last thing done. 7251da66f75SEd Tanous immediateLogMatcher = nullptr; 7261da66f75SEd Tanous return; 7271da66f75SEd Tanous } 7281da66f75SEd Tanous std::string t = getLogCreatedTime(j); 729*e1f26343SJason M. Bills asyncResp->res.jsonValue = { 7301da66f75SEd Tanous {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 7311da66f75SEd Tanous {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 7321da66f75SEd Tanous {"Name", "CPU Debug Log"}, 7331da66f75SEd Tanous {"EntryType", "Oem"}, 7341da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 7351da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 7361da66f75SEd Tanous {"Created", std::move(t)}}; 7371da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 7381da66f75SEd Tanous // match object inside which this lambda is executing. Once it is 7391da66f75SEd Tanous // set to nullptr, the match object will be destroyed and the lambda 7401da66f75SEd Tanous // will lose its context, including res, so it needs to be the last 7411da66f75SEd Tanous // thing done. 7421da66f75SEd Tanous immediateLogMatcher = nullptr; 7431da66f75SEd Tanous }; 7441da66f75SEd Tanous immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>( 7451da66f75SEd Tanous *crow::connections::systemBus, 7461da66f75SEd Tanous sdbusplus::bus::match::rules::interfacesAdded() + 7474ed77cd5SEd Tanous sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath), 7481da66f75SEd Tanous std::move(immediateLogMatcherCallback)); 7491da66f75SEd Tanous 7501da66f75SEd Tanous auto generateImmediateLogCallback = 751*e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 7521da66f75SEd Tanous const std::string &resp) { 7531da66f75SEd Tanous if (ec) 7541da66f75SEd Tanous { 7551da66f75SEd Tanous if (ec.value() == 7561da66f75SEd Tanous boost::system::errc::operation_not_supported) 7571da66f75SEd Tanous { 7581da66f75SEd Tanous messages::addMessageToJson( 759*e1f26343SJason M. Bills asyncResp->res.jsonValue, 760*e1f26343SJason M. Bills messages::resourceInStandby(), "/CpuLog.Immediate"); 761*e1f26343SJason M. Bills asyncResp->res.result( 7621da66f75SEd Tanous boost::beast::http::status::service_unavailable); 7631da66f75SEd Tanous } 7641da66f75SEd Tanous else 7651da66f75SEd Tanous { 766*e1f26343SJason M. Bills asyncResp->res.result( 7671da66f75SEd Tanous boost::beast::http::status::internal_server_error); 7681da66f75SEd Tanous } 7691da66f75SEd Tanous boost::system::error_code timeoutec; 7701da66f75SEd Tanous timeout.cancel(timeoutec); 7711da66f75SEd Tanous if (timeoutec) 7721da66f75SEd Tanous { 7731da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " 7741da66f75SEd Tanous << timeoutec; 7751da66f75SEd Tanous } 7761da66f75SEd Tanous immediateLogMatcher = nullptr; 7771da66f75SEd Tanous return; 7781da66f75SEd Tanous } 7791da66f75SEd Tanous }; 7801da66f75SEd Tanous crow::connections::systemBus->async_method_call( 7814ed77cd5SEd Tanous std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath, 7824ed77cd5SEd Tanous cpuLogImmediateInterface, "GenerateImmediateLog"); 7831da66f75SEd Tanous } 7841da66f75SEd Tanous }; 7851da66f75SEd Tanous 786*e1f26343SJason M. Bills class SendRawPECI : public Node 7871da66f75SEd Tanous { 7881da66f75SEd Tanous public: 789*e1f26343SJason M. Bills SendRawPECI(CrowApp &app) : 7904ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 791*e1f26343SJason M. Bills "CpuLog.SendRawPeci/") 7921da66f75SEd Tanous { 7931da66f75SEd Tanous entityPrivileges = { 7941da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 7951da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 7961da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 7971da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 7981da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 7991da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 8001da66f75SEd Tanous } 8011da66f75SEd Tanous 8021da66f75SEd Tanous private: 8031da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 8041da66f75SEd Tanous const std::vector<std::string> ¶ms) override 8051da66f75SEd Tanous { 806*e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 8071da66f75SEd Tanous // Get the Raw PECI command from the request 808*e1f26343SJason M. Bills nlohmann::json rawPECICmd; 809*e1f26343SJason M. Bills if (!json_util::processJsonFromRequest(res, req, rawPECICmd)) 8101da66f75SEd Tanous { 8111da66f75SEd Tanous return; 8121da66f75SEd Tanous } 8131da66f75SEd Tanous // Get the Client Address from the request 814*e1f26343SJason M. Bills nlohmann::json::const_iterator caIt = rawPECICmd.find("ClientAddress"); 815*e1f26343SJason M. Bills if (caIt == rawPECICmd.end()) 8161da66f75SEd Tanous { 8171da66f75SEd Tanous messages::addMessageToJson( 818*e1f26343SJason M. Bills asyncResp->res.jsonValue, 819*e1f26343SJason M. Bills messages::propertyMissing("ClientAddress"), "/ClientAddress"); 820*e1f26343SJason M. Bills asyncResp->res.result(boost::beast::http::status::bad_request); 8211da66f75SEd Tanous return; 8221da66f75SEd Tanous } 8231da66f75SEd Tanous const uint64_t *ca = caIt->get_ptr<const uint64_t *>(); 8241da66f75SEd Tanous if (ca == nullptr) 8251da66f75SEd Tanous { 8261da66f75SEd Tanous messages::addMessageToJson( 827*e1f26343SJason M. Bills asyncResp->res.jsonValue, 8281da66f75SEd Tanous messages::propertyValueTypeError(caIt->dump(), "ClientAddress"), 8291da66f75SEd Tanous "/ClientAddress"); 830*e1f26343SJason M. Bills asyncResp->res.result(boost::beast::http::status::bad_request); 8311da66f75SEd Tanous return; 8321da66f75SEd Tanous } 8331da66f75SEd Tanous // Get the Read Length from the request 8341da66f75SEd Tanous const uint8_t clientAddress = static_cast<uint8_t>(*ca); 835*e1f26343SJason M. Bills nlohmann::json::const_iterator rlIt = rawPECICmd.find("ReadLength"); 836*e1f26343SJason M. Bills if (rlIt == rawPECICmd.end()) 8371da66f75SEd Tanous { 838*e1f26343SJason M. Bills messages::addMessageToJson(asyncResp->res.jsonValue, 8391da66f75SEd Tanous messages::propertyMissing("ReadLength"), 8401da66f75SEd Tanous "/ReadLength"); 841*e1f26343SJason M. Bills asyncResp->res.result(boost::beast::http::status::bad_request); 8421da66f75SEd Tanous return; 8431da66f75SEd Tanous } 8441da66f75SEd Tanous const uint64_t *rl = rlIt->get_ptr<const uint64_t *>(); 8451da66f75SEd Tanous if (rl == nullptr) 8461da66f75SEd Tanous { 8471da66f75SEd Tanous messages::addMessageToJson( 848*e1f26343SJason M. Bills asyncResp->res.jsonValue, 8491da66f75SEd Tanous messages::propertyValueTypeError(rlIt->dump(), "ReadLength"), 8501da66f75SEd Tanous "/ReadLength"); 851*e1f26343SJason M. Bills asyncResp->res.result(boost::beast::http::status::bad_request); 8521da66f75SEd Tanous return; 8531da66f75SEd Tanous } 8541da66f75SEd Tanous // Get the PECI Command from the request 8551da66f75SEd Tanous const uint32_t readLength = static_cast<uint32_t>(*rl); 856*e1f26343SJason M. Bills nlohmann::json::const_iterator pcIt = rawPECICmd.find("PECICommand"); 857*e1f26343SJason M. Bills if (pcIt == rawPECICmd.end()) 8581da66f75SEd Tanous { 859*e1f26343SJason M. Bills messages::addMessageToJson(asyncResp->res.jsonValue, 8601da66f75SEd Tanous messages::propertyMissing("PECICommand"), 8611da66f75SEd Tanous "/PECICommand"); 862*e1f26343SJason M. Bills asyncResp->res.result(boost::beast::http::status::bad_request); 8631da66f75SEd Tanous return; 8641da66f75SEd Tanous } 8651da66f75SEd Tanous std::vector<uint8_t> peciCommand; 8661da66f75SEd Tanous for (auto pc : *pcIt) 8671da66f75SEd Tanous { 8681da66f75SEd Tanous const uint64_t *val = pc.get_ptr<const uint64_t *>(); 8691da66f75SEd Tanous if (val == nullptr) 8701da66f75SEd Tanous { 8711da66f75SEd Tanous messages::addMessageToJson( 872*e1f26343SJason M. Bills asyncResp->res.jsonValue, 8731da66f75SEd Tanous messages::propertyValueTypeError( 8741da66f75SEd Tanous pc.dump(), 8751da66f75SEd Tanous "PECICommand/" + std::to_string(peciCommand.size())), 8761da66f75SEd Tanous "/PECICommand"); 877*e1f26343SJason M. Bills asyncResp->res.result(boost::beast::http::status::bad_request); 8781da66f75SEd Tanous return; 8791da66f75SEd Tanous } 8801da66f75SEd Tanous peciCommand.push_back(static_cast<uint8_t>(*val)); 8811da66f75SEd Tanous } 8821da66f75SEd Tanous // Callback to return the Raw PECI response 883*e1f26343SJason M. Bills auto sendRawPECICallback = 884*e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 8851da66f75SEd Tanous const std::vector<uint8_t> &resp) { 8861da66f75SEd Tanous if (ec) 8871da66f75SEd Tanous { 8881da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to send PECI command ec: " 8891da66f75SEd Tanous << ec.message(); 890*e1f26343SJason M. Bills asyncResp->res.result( 891*e1f26343SJason M. Bills boost::beast::http::status::internal_server_error); 8921da66f75SEd Tanous return; 8931da66f75SEd Tanous } 894*e1f26343SJason M. Bills asyncResp->res.jsonValue = {{"Name", "PECI Command Response"}, 8951da66f75SEd Tanous {"PECIResponse", resp}}; 8961da66f75SEd Tanous }; 8971da66f75SEd Tanous // Call the SendRawPECI command with the provided data 8981da66f75SEd Tanous crow::connections::systemBus->async_method_call( 899*e1f26343SJason M. Bills std::move(sendRawPECICallback), cpuLogObject, cpuLogPath, 900*e1f26343SJason M. Bills cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength, 9014ed77cd5SEd Tanous peciCommand); 9021da66f75SEd Tanous } 9031da66f75SEd Tanous }; 9041da66f75SEd Tanous 9051da66f75SEd Tanous } // namespace redfish 906