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 20e1f26343SJason M. Bills #include <systemd/sd-journal.h> 21e1f26343SJason M. Bills 221da66f75SEd Tanous #include <boost/container/flat_map.hpp> 23e1f26343SJason 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"; 35e1f26343SJason 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 = { 51e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 52e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 53e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 54e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 55e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 56e1f26343SJason 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 { 66e1f26343SJason 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 69e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 701da66f75SEd Tanous "#LogServiceCollection.LogServiceCollection"; 71e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 721da66f75SEd Tanous "/redfish/v1/" 731da66f75SEd Tanous "$metadata#LogServiceCollection.LogServiceCollection"; 74e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 75e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices"; 76e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection"; 77e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 781da66f75SEd Tanous "Collection of LogServices for this Manager"; 79e1f26343SJason M. Bills nlohmann::json &logserviceArray = asyncResp->res.jsonValue["Members"]; 801da66f75SEd Tanous logserviceArray = nlohmann::json::array(); 81e1f26343SJason M. Bills logserviceArray.push_back( 82e1f26343SJason 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 87e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 88e1f26343SJason M. Bills logserviceArray.size(); 891da66f75SEd Tanous } 901da66f75SEd Tanous }; 911da66f75SEd Tanous 92e1f26343SJason M. Bills class BMCLogService : public Node 931da66f75SEd Tanous { 941da66f75SEd Tanous public: 951da66f75SEd Tanous template <typename CrowApp> 96e1f26343SJason M. Bills BMCLogService(CrowApp &app) : 97e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/") 98e1f26343SJason M. Bills { 99e1f26343SJason M. Bills // Set the id for SubRoute 100e1f26343SJason M. Bills Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/BmcLog"; 101e1f26343SJason M. Bills entityPrivileges = { 102e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 103e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 104e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 105e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 106e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 107e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 108e1f26343SJason M. Bills } 109e1f26343SJason M. Bills 110e1f26343SJason M. Bills private: 111e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 112e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 113e1f26343SJason M. Bills { 114e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 115e1f26343SJason M. Bills // Copy over the static data to include the entries added by SubRoute 116e1f26343SJason M. Bills asyncResp->res.jsonValue = Node::json; 117e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 118e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 119e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 120e1f26343SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 121e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Log Service"; 122e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = "BMC Log Service"; 123e1f26343SJason M. Bills asyncResp->res.jsonValue["Id"] = "BMC Log"; 124e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 125e1f26343SJason M. Bills } 126e1f26343SJason M. Bills }; 127e1f26343SJason M. Bills 128e1f26343SJason M. Bills static int fillBMCLogEntryJson(const std::string &bmcLogEntryID, 129e1f26343SJason M. Bills sd_journal *journal, 130e1f26343SJason M. Bills nlohmann::json &bmcLogEntryJson) 131e1f26343SJason M. Bills { 132e1f26343SJason M. Bills // Get the Log Entry contents 133e1f26343SJason M. Bills int ret = 0; 134e1f26343SJason M. Bills const char *data = nullptr; 135e1f26343SJason M. Bills size_t length = 0; 136e1f26343SJason M. Bills 137e1f26343SJason M. Bills ret = 138e1f26343SJason M. Bills sd_journal_get_data(journal, "MESSAGE", (const void **)&data, &length); 139e1f26343SJason M. Bills if (ret < 0) 140e1f26343SJason M. Bills { 141e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret); 142e1f26343SJason M. Bills return 1; 143e1f26343SJason M. Bills } 144e1f26343SJason M. Bills boost::string_view msg; 145e1f26343SJason M. Bills msg = boost::string_view(data, length); 146e1f26343SJason M. Bills // Only use the content after the "=" character. 147e1f26343SJason M. Bills msg.remove_prefix(std::min(msg.find("=") + 1, msg.size())); 148e1f26343SJason M. Bills 149e1f26343SJason M. Bills // Get the severity from the PRIORITY field 150e1f26343SJason M. Bills boost::string_view priority; 151e1f26343SJason M. Bills int severity = 8; // Default to an invalid priority 152e1f26343SJason M. Bills ret = 153e1f26343SJason M. Bills sd_journal_get_data(journal, "PRIORITY", (const void **)&data, &length); 154e1f26343SJason M. Bills if (ret < 0) 155e1f26343SJason M. Bills { 156e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret); 157e1f26343SJason M. Bills return 1; 158e1f26343SJason M. Bills } 159e1f26343SJason M. Bills priority = boost::string_view(data, length); 160e1f26343SJason M. Bills // Check length for sanity. Must be a single digit in the form 161e1f26343SJason M. Bills // "PRIORITY=[0-7]" 162e1f26343SJason M. Bills if (priority.size() > sizeof("PRIORITY=0")) 163e1f26343SJason M. Bills { 164e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Invalid PRIORITY field length"; 165e1f26343SJason M. Bills return 1; 166e1f26343SJason M. Bills } 167e1f26343SJason M. Bills // Only use the content after the "=" character. 168e1f26343SJason M. Bills priority.remove_prefix(std::min(priority.find("=") + 1, priority.size())); 169e1f26343SJason M. Bills severity = strtol(priority.data(), nullptr, 10); 170e1f26343SJason M. Bills 171e1f26343SJason M. Bills // Get the Created time from the timestamp 172e1f26343SJason M. Bills // Get the entry timestamp 173e1f26343SJason M. Bills uint64_t timestamp = 0; 174e1f26343SJason M. Bills ret = sd_journal_get_realtime_usec(journal, ×tamp); 175e1f26343SJason M. Bills if (ret < 0) 176e1f26343SJason M. Bills { 177e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 178e1f26343SJason M. Bills << strerror(-ret); 179e1f26343SJason M. Bills } 180e1f26343SJason M. Bills time_t t = 181e1f26343SJason M. Bills static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s 182e1f26343SJason M. Bills struct tm *loctime = localtime(&t); 183e1f26343SJason M. Bills char entryTime[64] = {}; 184e1f26343SJason M. Bills if (NULL != loctime) 185e1f26343SJason M. Bills { 186e1f26343SJason M. Bills strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime); 187e1f26343SJason M. Bills } 188e1f26343SJason M. Bills // Insert the ':' into the timezone 189e1f26343SJason M. Bills boost::string_view t1(entryTime); 190e1f26343SJason M. Bills boost::string_view t2(entryTime); 191e1f26343SJason M. Bills if (t1.size() > 2 && t2.size() > 2) 192e1f26343SJason M. Bills { 193e1f26343SJason M. Bills t1.remove_suffix(2); 194e1f26343SJason M. Bills t2.remove_prefix(t2.size() - 2); 195e1f26343SJason M. Bills } 196e1f26343SJason M. Bills const std::string entryTimeStr(t1.to_string() + ":" + t2.to_string()); 197e1f26343SJason M. Bills 198e1f26343SJason M. Bills // Fill in the log entry with the gathered data 199e1f26343SJason M. Bills bmcLogEntryJson = { 200e1f26343SJason M. Bills {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 201e1f26343SJason M. Bills {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 202e1f26343SJason M. Bills {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/" + 203e1f26343SJason M. Bills bmcLogEntryID}, 204e1f26343SJason M. Bills {"Name", "BMC Journal Entry"}, 205e1f26343SJason M. Bills {"Id", bmcLogEntryID}, 206e1f26343SJason M. Bills {"Message", msg.to_string()}, 207e1f26343SJason M. Bills {"EntryType", "Oem"}, 208e1f26343SJason M. Bills {"Severity", 209e1f26343SJason M. Bills severity <= 2 ? "Critical" 210e1f26343SJason M. Bills : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""}, 211e1f26343SJason M. Bills {"OemRecordFormat", "Intel BMC Journal Entry"}, 212e1f26343SJason M. Bills {"Created", std::move(entryTimeStr)}}; 213e1f26343SJason M. Bills return 0; 214e1f26343SJason M. Bills } 215e1f26343SJason M. Bills 216e1f26343SJason M. Bills class BMCLogEntryCollection : public Node 217e1f26343SJason M. Bills { 218e1f26343SJason M. Bills public: 219e1f26343SJason M. Bills template <typename CrowApp> 220e1f26343SJason M. Bills BMCLogEntryCollection(CrowApp &app) : 221e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/") 222e1f26343SJason M. Bills { 223e1f26343SJason M. Bills // Collections use static ID for SubRoute to add to its parent, but only 224e1f26343SJason M. Bills // load dynamic data so the duplicate static members don't get displayed 225e1f26343SJason M. Bills Node::json["@odata.id"] = 226e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; 227e1f26343SJason M. Bills entityPrivileges = { 228e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 229e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 230e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 231e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 232e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 233e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 234e1f26343SJason M. Bills } 235e1f26343SJason M. Bills 236e1f26343SJason M. Bills private: 237e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 238e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 239e1f26343SJason M. Bills { 240e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 241e1f26343SJason M. Bills // Collections don't include the static data added by SubRoute because 242e1f26343SJason M. Bills // it has a duplicate entry for members 243e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 244e1f26343SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 245e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 246e1f26343SJason M. Bills "/redfish/v1/" 247e1f26343SJason M. Bills "$metadata#LogEntryCollection.LogEntryCollection"; 248e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 249e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; 250e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; 251e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 252e1f26343SJason M. Bills "Collection of BMC Journal Entries"; 253e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 254e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 255e1f26343SJason M. Bills 256e1f26343SJason M. Bills // Go through the journal and use the timestamp to create a unique ID 257e1f26343SJason M. Bills // for each entry 258e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 259e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 260e1f26343SJason M. Bills if (ret < 0) 261e1f26343SJason M. Bills { 262e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 263*f12894f8SJason M. Bills messages::internalError(asyncResp->res); 264e1f26343SJason M. Bills return; 265e1f26343SJason M. Bills } 266e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 267e1f26343SJason M. Bills journalTmp, sd_journal_close); 268e1f26343SJason M. Bills journalTmp = nullptr; 269e1f26343SJason M. Bills uint64_t prevTs = 0; 270e1f26343SJason M. Bills int index = 0; 271e1f26343SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 272e1f26343SJason M. Bills { 273e1f26343SJason M. Bills // Get the entry timestamp 274e1f26343SJason M. Bills uint64_t curTs = 0; 275e1f26343SJason M. Bills ret = sd_journal_get_realtime_usec(journal.get(), &curTs); 276e1f26343SJason M. Bills if (ret < 0) 277e1f26343SJason M. Bills { 278e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 279e1f26343SJason M. Bills << strerror(-ret); 280e1f26343SJason M. Bills continue; 281e1f26343SJason M. Bills } 282e1f26343SJason M. Bills // If the timestamp isn't unique, increment the index 283e1f26343SJason M. Bills if (curTs == prevTs) 284e1f26343SJason M. Bills { 285e1f26343SJason M. Bills index++; 286e1f26343SJason M. Bills } 287e1f26343SJason M. Bills else 288e1f26343SJason M. Bills { 289e1f26343SJason M. Bills // Otherwise, reset it 290e1f26343SJason M. Bills index = 0; 291e1f26343SJason M. Bills } 292e1f26343SJason M. Bills // Save the timestamp 293e1f26343SJason M. Bills prevTs = curTs; 294e1f26343SJason M. Bills 295e1f26343SJason M. Bills std::string idStr(std::to_string(curTs)); 296e1f26343SJason M. Bills if (index > 0) 297e1f26343SJason M. Bills { 298e1f26343SJason M. Bills idStr += "_" + std::to_string(index); 299e1f26343SJason M. Bills } 300e1f26343SJason M. Bills logEntryArray.push_back({}); 301e1f26343SJason M. Bills nlohmann::json &bmcLogEntry = logEntryArray.back(); 302e1f26343SJason M. Bills if (fillBMCLogEntryJson(idStr, journal.get(), bmcLogEntry) != 0) 303e1f26343SJason M. Bills { 304*f12894f8SJason M. Bills messages::internalError(asyncResp->res); 305e1f26343SJason M. Bills return; 306e1f26343SJason M. Bills } 307e1f26343SJason M. Bills } 308e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = logEntryArray.size(); 309e1f26343SJason M. Bills } 310e1f26343SJason M. Bills }; 311e1f26343SJason M. Bills 312e1f26343SJason M. Bills class BMCLogEntry : public Node 313e1f26343SJason M. Bills { 314e1f26343SJason M. Bills public: 315e1f26343SJason M. Bills BMCLogEntry(CrowApp &app) : 316e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/<str>/", 317e1f26343SJason M. Bills std::string()) 318e1f26343SJason M. Bills { 319e1f26343SJason M. Bills entityPrivileges = { 320e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 321e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 322e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 323e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 324e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 325e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 326e1f26343SJason M. Bills } 327e1f26343SJason M. Bills 328e1f26343SJason M. Bills private: 329e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 330e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 331e1f26343SJason M. Bills { 332e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 333e1f26343SJason M. Bills if (params.size() != 1) 334e1f26343SJason M. Bills { 335*f12894f8SJason M. Bills messages::internalError(asyncResp->res); 336e1f26343SJason M. Bills return; 337e1f26343SJason M. Bills } 338e1f26343SJason M. Bills // Convert the unique ID back to a timestamp to find the entry 339e1f26343SJason M. Bills boost::string_view tsStr(params[0]); 340e1f26343SJason M. Bills boost::string_view indexStr(params[0]); 341e1f26343SJason M. Bills uint64_t ts = 0; 342e1f26343SJason M. Bills uint16_t index = 0; 343e1f26343SJason M. Bills auto underscorePos = tsStr.find("_"); 344e1f26343SJason M. Bills if (underscorePos == tsStr.npos) 345e1f26343SJason M. Bills { 346e1f26343SJason M. Bills // Timestamp has no index 347e1f26343SJason M. Bills ts = strtoull(tsStr.data(), nullptr, 10); 348e1f26343SJason M. Bills } 349e1f26343SJason M. Bills else 350e1f26343SJason M. Bills { 351e1f26343SJason M. Bills // Timestamp has an index 352e1f26343SJason M. Bills tsStr.remove_suffix(tsStr.size() - underscorePos + 1); 353e1f26343SJason M. Bills ts = strtoull(tsStr.data(), nullptr, 10); 354e1f26343SJason M. Bills indexStr.remove_prefix(underscorePos + 1); 355e1f26343SJason M. Bills index = 356e1f26343SJason M. Bills static_cast<uint16_t>(strtoul(indexStr.data(), nullptr, 10)); 357e1f26343SJason M. Bills } 358e1f26343SJason M. Bills 359e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 360e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 361e1f26343SJason M. Bills if (ret < 0) 362e1f26343SJason M. Bills { 363e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 364*f12894f8SJason M. Bills messages::internalError(asyncResp->res); 365e1f26343SJason M. Bills return; 366e1f26343SJason M. Bills } 367e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 368e1f26343SJason M. Bills journalTmp, sd_journal_close); 369e1f26343SJason M. Bills journalTmp = nullptr; 370e1f26343SJason M. Bills // Go to the timestamp in the log and move to the entry at the index 371e1f26343SJason M. Bills ret = sd_journal_seek_realtime_usec(journal.get(), ts); 372e1f26343SJason M. Bills for (int i = 0; i <= index; i++) 373e1f26343SJason M. Bills { 374e1f26343SJason M. Bills sd_journal_next(journal.get()); 375e1f26343SJason M. Bills } 376e1f26343SJason M. Bills if (fillBMCLogEntryJson(params[0], journal.get(), 377e1f26343SJason M. Bills asyncResp->res.jsonValue) != 0) 378e1f26343SJason M. Bills { 379*f12894f8SJason M. Bills messages::internalError(asyncResp->res); 380e1f26343SJason M. Bills return; 381e1f26343SJason M. Bills } 382e1f26343SJason M. Bills } 383e1f26343SJason M. Bills }; 384e1f26343SJason M. Bills 385e1f26343SJason M. Bills class CPULogService : public Node 386e1f26343SJason M. Bills { 387e1f26343SJason M. Bills public: 388e1f26343SJason M. Bills template <typename CrowApp> 389e1f26343SJason M. Bills CPULogService(CrowApp &app) : 390e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/") 3911da66f75SEd Tanous { 3921da66f75SEd Tanous // Set the id for SubRoute 3934ed77cd5SEd Tanous Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/CpuLog"; 3941da66f75SEd Tanous entityPrivileges = { 395e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 396e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 397e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 398e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 399e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 400e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 4011da66f75SEd Tanous } 4021da66f75SEd Tanous 4031da66f75SEd Tanous private: 4041da66f75SEd Tanous /** 4051da66f75SEd Tanous * Functions triggers appropriate requests on DBus 4061da66f75SEd Tanous */ 4071da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 4081da66f75SEd Tanous const std::vector<std::string> ¶ms) override 4091da66f75SEd Tanous { 410e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 4111da66f75SEd Tanous // Copy over the static data to include the entries added by SubRoute 412e1f26343SJason M. Bills asyncResp->res.jsonValue = Node::json; 413e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 414e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 415e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 416e1f26343SJason M. Bills "/redfish/v1/" 4171da66f75SEd Tanous "$metadata#LogService.LogService"; 418e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service"; 419e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = "CPU Log Service"; 420e1f26343SJason M. Bills asyncResp->res.jsonValue["Id"] = "CPU Log"; 421e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 422e1f26343SJason M. Bills asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 423e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"] = { 4241da66f75SEd Tanous {"Oem", 4251da66f75SEd Tanous {{"#CpuLog.Immediate", 4261da66f75SEd Tanous {{"target", 4274ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 4281da66f75SEd Tanous "CpuLog.Immediate"}}}}}}; 4291da66f75SEd Tanous 4301da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI 431e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"]["Oem"].push_back( 4321da66f75SEd Tanous {"#CpuLog.SendRawPeci", 4331da66f75SEd Tanous {{"target", 4344ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 4351da66f75SEd Tanous "CpuLog.SendRawPeci"}}}); 4361da66f75SEd Tanous #endif 4371da66f75SEd Tanous } 4381da66f75SEd Tanous }; 4391da66f75SEd Tanous 440e1f26343SJason M. Bills class CPULogEntryCollection : public Node 4411da66f75SEd Tanous { 4421da66f75SEd Tanous public: 4431da66f75SEd Tanous template <typename CrowApp> 444e1f26343SJason M. Bills CPULogEntryCollection(CrowApp &app) : 445e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/") 4461da66f75SEd Tanous { 4471da66f75SEd Tanous // Collections use static ID for SubRoute to add to its parent, but only 4481da66f75SEd Tanous // load dynamic data so the duplicate static members don't get displayed 4491da66f75SEd Tanous Node::json["@odata.id"] = 4504ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries"; 4511da66f75SEd Tanous entityPrivileges = { 452e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 453e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 454e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 455e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 456e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 457e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 4581da66f75SEd Tanous } 4591da66f75SEd Tanous 4601da66f75SEd Tanous private: 4611da66f75SEd Tanous /** 4621da66f75SEd Tanous * Functions triggers appropriate requests on DBus 4631da66f75SEd Tanous */ 4641da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 4651da66f75SEd Tanous const std::vector<std::string> ¶ms) override 4661da66f75SEd Tanous { 467e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 4681da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 4691da66f75SEd Tanous // it has a duplicate entry for members 470e1f26343SJason M. Bills auto getLogEntriesCallback = [asyncResp]( 471e1f26343SJason M. Bills const boost::system::error_code ec, 4721da66f75SEd Tanous const std::vector<std::string> &resp) { 4731da66f75SEd Tanous if (ec) 4741da66f75SEd Tanous { 4751da66f75SEd Tanous if (ec.value() != 4761da66f75SEd Tanous boost::system::errc::no_such_file_or_directory) 4771da66f75SEd Tanous { 4781da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to get entries ec: " 4791da66f75SEd Tanous << ec.message(); 480*f12894f8SJason M. Bills messages::internalError(asyncResp->res); 4811da66f75SEd Tanous return; 4821da66f75SEd Tanous } 4831da66f75SEd Tanous } 484e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 4851da66f75SEd Tanous "#LogEntryCollection.LogEntryCollection"; 486e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 4871da66f75SEd Tanous "/redfish/v1/" 4881da66f75SEd Tanous "$metadata#LogEntryCollection.LogEntryCollection"; 489e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 490e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries"; 491e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries"; 492e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 493e1f26343SJason M. Bills "Collection of CPU Log Entries"; 494e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 495e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 4961da66f75SEd Tanous for (const std::string &objpath : resp) 4971da66f75SEd Tanous { 4981da66f75SEd Tanous // Don't list the immediate log 4994ed77cd5SEd Tanous if (objpath.compare(cpuLogImmediatePath) == 0) 5001da66f75SEd Tanous { 5011da66f75SEd Tanous continue; 5021da66f75SEd Tanous } 5034ed77cd5SEd Tanous std::size_t lastPos = objpath.rfind("/"); 5044ed77cd5SEd Tanous if (lastPos != std::string::npos) 5051da66f75SEd Tanous { 506e1f26343SJason M. Bills logEntryArray.push_back( 507e1f26343SJason M. Bills {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/" 508e1f26343SJason M. Bills "CpuLog/Entries/" + 5094ed77cd5SEd Tanous objpath.substr(lastPos + 1)}}); 5101da66f75SEd Tanous } 5111da66f75SEd Tanous } 512e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 513e1f26343SJason M. Bills logEntryArray.size(); 5141da66f75SEd Tanous }; 5151da66f75SEd Tanous crow::connections::systemBus->async_method_call( 5161da66f75SEd Tanous std::move(getLogEntriesCallback), 5171da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", 5181da66f75SEd Tanous "/xyz/openbmc_project/object_mapper", 5191da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0, 5204ed77cd5SEd Tanous std::array<const char *, 1>{cpuLogInterface}); 5211da66f75SEd Tanous } 5221da66f75SEd Tanous }; 5231da66f75SEd Tanous 5241da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog) 5251da66f75SEd Tanous { 5261da66f75SEd Tanous nlohmann::json::const_iterator metaIt = cpuLog.find("metadata"); 5271da66f75SEd Tanous if (metaIt != cpuLog.end()) 5281da66f75SEd Tanous { 5291da66f75SEd Tanous nlohmann::json::const_iterator tsIt = metaIt->find("timestamp"); 5301da66f75SEd Tanous if (tsIt != metaIt->end()) 5311da66f75SEd Tanous { 5321da66f75SEd Tanous const std::string *logTime = tsIt->get_ptr<const std::string *>(); 5331da66f75SEd Tanous if (logTime != nullptr) 5341da66f75SEd Tanous { 5351da66f75SEd Tanous return *logTime; 5361da66f75SEd Tanous } 5371da66f75SEd Tanous } 5381da66f75SEd Tanous } 5391da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to find log timestamp"; 5401da66f75SEd Tanous 5411da66f75SEd Tanous return std::string(); 5421da66f75SEd Tanous } 5431da66f75SEd Tanous 544e1f26343SJason M. Bills class CPULogEntry : public Node 5451da66f75SEd Tanous { 5461da66f75SEd Tanous public: 547e1f26343SJason M. Bills CPULogEntry(CrowApp &app) : 5484ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/", 5491da66f75SEd Tanous std::string()) 5501da66f75SEd Tanous { 5511da66f75SEd Tanous entityPrivileges = { 552e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 553e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 554e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 555e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 556e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 557e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 5581da66f75SEd Tanous } 5591da66f75SEd Tanous 5601da66f75SEd Tanous private: 5611da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 5621da66f75SEd Tanous const std::vector<std::string> ¶ms) override 5631da66f75SEd Tanous { 564e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 5651da66f75SEd Tanous if (params.size() != 1) 5661da66f75SEd Tanous { 567*f12894f8SJason M. Bills messages::internalError(asyncResp->res); 5681da66f75SEd Tanous return; 5691da66f75SEd Tanous } 5704ed77cd5SEd Tanous const uint8_t logId = std::atoi(params[0].c_str()); 571e1f26343SJason M. Bills auto getStoredLogCallback = 572e1f26343SJason M. Bills [asyncResp, 5734ed77cd5SEd Tanous logId](const boost::system::error_code ec, 574e1f26343SJason M. Bills const sdbusplus::message::variant<std::string> &resp) { 5751da66f75SEd Tanous if (ec) 5761da66f75SEd Tanous { 577e1f26343SJason M. Bills BMCWEB_LOG_DEBUG << "failed to get log ec: " 578e1f26343SJason M. Bills << ec.message(); 579*f12894f8SJason M. Bills messages::internalError(asyncResp->res); 5801da66f75SEd Tanous return; 5811da66f75SEd Tanous } 582e1f26343SJason M. Bills const std::string *log = 583e1f26343SJason M. Bills mapbox::getPtr<const std::string>(resp); 5841da66f75SEd Tanous if (log == nullptr) 5851da66f75SEd Tanous { 586*f12894f8SJason M. Bills messages::internalError(asyncResp->res); 5871da66f75SEd Tanous return; 5881da66f75SEd Tanous } 5891da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 5901da66f75SEd Tanous if (j.is_discarded()) 5911da66f75SEd Tanous { 592*f12894f8SJason M. Bills messages::internalError(asyncResp->res); 5931da66f75SEd Tanous return; 5941da66f75SEd Tanous } 5951da66f75SEd Tanous std::string t = getLogCreatedTime(j); 596e1f26343SJason M. Bills asyncResp->res.jsonValue = { 5971da66f75SEd Tanous {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 598e1f26343SJason M. Bills {"@odata.context", 599e1f26343SJason M. Bills "/redfish/v1/$metadata#LogEntry.LogEntry"}, 6001da66f75SEd Tanous {"@odata.id", 6014ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" + 6024ed77cd5SEd Tanous std::to_string(logId)}, 6031da66f75SEd Tanous {"Name", "CPU Debug Log"}, 6044ed77cd5SEd Tanous {"Id", logId}, 6051da66f75SEd Tanous {"EntryType", "Oem"}, 6061da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 6071da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 6081da66f75SEd Tanous {"Created", std::move(t)}}; 6091da66f75SEd Tanous }; 6101da66f75SEd Tanous crow::connections::systemBus->async_method_call( 6114ed77cd5SEd Tanous std::move(getStoredLogCallback), cpuLogObject, 6124ed77cd5SEd Tanous cpuLogPath + std::string("/") + std::to_string(logId), 6134ed77cd5SEd Tanous "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log"); 6141da66f75SEd Tanous } 6151da66f75SEd Tanous }; 6161da66f75SEd Tanous 617e1f26343SJason M. Bills class ImmediateCPULog : public Node 6181da66f75SEd Tanous { 6191da66f75SEd Tanous public: 620e1f26343SJason M. Bills ImmediateCPULog(CrowApp &app) : 6214ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 622e1f26343SJason M. Bills "CpuLog.Immediate/") 6231da66f75SEd Tanous { 6241da66f75SEd Tanous entityPrivileges = { 625e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 626e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 627e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 628e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 629e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 630e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 6311da66f75SEd Tanous } 6321da66f75SEd Tanous 6331da66f75SEd Tanous private: 6341da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 6351da66f75SEd Tanous const std::vector<std::string> ¶ms) override 6361da66f75SEd Tanous { 637e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 6381da66f75SEd Tanous static std::unique_ptr<sdbusplus::bus::match::match> 6391da66f75SEd Tanous immediateLogMatcher; 6401da66f75SEd Tanous 6411da66f75SEd Tanous // Only allow one Immediate Log request at a time 6421da66f75SEd Tanous if (immediateLogMatcher != nullptr) 6431da66f75SEd Tanous { 644e1f26343SJason M. Bills asyncResp->res.addHeader("Retry-After", "30"); 645*f12894f8SJason M. Bills messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); 6461da66f75SEd Tanous return; 6471da66f75SEd Tanous } 6481da66f75SEd Tanous // Make this static so it survives outside this method 6491da66f75SEd Tanous static boost::asio::deadline_timer timeout(*req.ioService); 6501da66f75SEd Tanous 6511da66f75SEd Tanous timeout.expires_from_now(boost::posix_time::seconds(30)); 652e1f26343SJason M. Bills timeout.async_wait([asyncResp](const boost::system::error_code &ec) { 6531da66f75SEd Tanous immediateLogMatcher = nullptr; 6541da66f75SEd Tanous if (ec) 6551da66f75SEd Tanous { 6561da66f75SEd Tanous // operation_aborted is expected if timer is canceled before 6571da66f75SEd Tanous // completion. 6581da66f75SEd Tanous if (ec != boost::asio::error::operation_aborted) 6591da66f75SEd Tanous { 6601da66f75SEd Tanous BMCWEB_LOG_ERROR << "Async_wait failed " << ec; 6611da66f75SEd Tanous } 6621da66f75SEd Tanous return; 6631da66f75SEd Tanous } 6641da66f75SEd Tanous BMCWEB_LOG_ERROR << "Timed out waiting for immediate log"; 6651da66f75SEd Tanous 666*f12894f8SJason M. Bills messages::internalError(asyncResp->res); 6671da66f75SEd Tanous }); 6681da66f75SEd Tanous 669e1f26343SJason M. Bills auto immediateLogMatcherCallback = [asyncResp]( 6701da66f75SEd Tanous sdbusplus::message::message &m) { 6711da66f75SEd Tanous BMCWEB_LOG_DEBUG << "Immediate log available match fired"; 6721da66f75SEd Tanous boost::system::error_code ec; 6731da66f75SEd Tanous timeout.cancel(ec); 6741da66f75SEd Tanous if (ec) 6751da66f75SEd Tanous { 6761da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " << ec; 6771da66f75SEd Tanous } 6784ed77cd5SEd Tanous sdbusplus::message::object_path objPath; 6791da66f75SEd Tanous boost::container::flat_map< 6801da66f75SEd Tanous std::string, 6811da66f75SEd Tanous boost::container::flat_map< 6821da66f75SEd Tanous std::string, sdbusplus::message::variant<std::string>>> 6834ed77cd5SEd Tanous interfacesAdded; 6844ed77cd5SEd Tanous m.read(objPath, interfacesAdded); 6851da66f75SEd Tanous const std::string *log = mapbox::getPtr<const std::string>( 6864ed77cd5SEd Tanous interfacesAdded[cpuLogInterface]["Log"]); 6871da66f75SEd Tanous if (log == nullptr) 6881da66f75SEd Tanous { 689*f12894f8SJason M. Bills messages::internalError(asyncResp->res); 6901da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 6911da66f75SEd Tanous // match object inside which this lambda is executing. Once it 6921da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 6931da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 6941da66f75SEd Tanous // be the last thing done. 6951da66f75SEd Tanous immediateLogMatcher = nullptr; 6961da66f75SEd Tanous return; 6971da66f75SEd Tanous } 6981da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 6991da66f75SEd Tanous if (j.is_discarded()) 7001da66f75SEd Tanous { 701*f12894f8SJason M. Bills messages::internalError(asyncResp->res); 7021da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 7031da66f75SEd Tanous // match object inside which this lambda is executing. Once it 7041da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 7051da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 7061da66f75SEd Tanous // be the last thing done. 7071da66f75SEd Tanous immediateLogMatcher = nullptr; 7081da66f75SEd Tanous return; 7091da66f75SEd Tanous } 7101da66f75SEd Tanous std::string t = getLogCreatedTime(j); 711e1f26343SJason M. Bills asyncResp->res.jsonValue = { 7121da66f75SEd Tanous {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 7131da66f75SEd Tanous {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 7141da66f75SEd Tanous {"Name", "CPU Debug Log"}, 7151da66f75SEd Tanous {"EntryType", "Oem"}, 7161da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 7171da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 7181da66f75SEd Tanous {"Created", std::move(t)}}; 7191da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 7201da66f75SEd Tanous // match object inside which this lambda is executing. Once it is 7211da66f75SEd Tanous // set to nullptr, the match object will be destroyed and the lambda 7221da66f75SEd Tanous // will lose its context, including res, so it needs to be the last 7231da66f75SEd Tanous // thing done. 7241da66f75SEd Tanous immediateLogMatcher = nullptr; 7251da66f75SEd Tanous }; 7261da66f75SEd Tanous immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>( 7271da66f75SEd Tanous *crow::connections::systemBus, 7281da66f75SEd Tanous sdbusplus::bus::match::rules::interfacesAdded() + 7294ed77cd5SEd Tanous sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath), 7301da66f75SEd Tanous std::move(immediateLogMatcherCallback)); 7311da66f75SEd Tanous 7321da66f75SEd Tanous auto generateImmediateLogCallback = 733e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 7341da66f75SEd Tanous const std::string &resp) { 7351da66f75SEd Tanous if (ec) 7361da66f75SEd Tanous { 7371da66f75SEd Tanous if (ec.value() == 7381da66f75SEd Tanous boost::system::errc::operation_not_supported) 7391da66f75SEd Tanous { 740*f12894f8SJason M. Bills messages::resourceInStandby(asyncResp->res); 7411da66f75SEd Tanous } 7421da66f75SEd Tanous else 7431da66f75SEd Tanous { 744*f12894f8SJason M. Bills messages::internalError(asyncResp->res); 7451da66f75SEd Tanous } 7461da66f75SEd Tanous boost::system::error_code timeoutec; 7471da66f75SEd Tanous timeout.cancel(timeoutec); 7481da66f75SEd Tanous if (timeoutec) 7491da66f75SEd Tanous { 7501da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " 7511da66f75SEd Tanous << timeoutec; 7521da66f75SEd Tanous } 7531da66f75SEd Tanous immediateLogMatcher = nullptr; 7541da66f75SEd Tanous return; 7551da66f75SEd Tanous } 7561da66f75SEd Tanous }; 7571da66f75SEd Tanous crow::connections::systemBus->async_method_call( 7584ed77cd5SEd Tanous std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath, 7594ed77cd5SEd Tanous cpuLogImmediateInterface, "GenerateImmediateLog"); 7601da66f75SEd Tanous } 7611da66f75SEd Tanous }; 7621da66f75SEd Tanous 763e1f26343SJason M. Bills class SendRawPECI : public Node 7641da66f75SEd Tanous { 7651da66f75SEd Tanous public: 766e1f26343SJason M. Bills SendRawPECI(CrowApp &app) : 7674ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 768e1f26343SJason M. Bills "CpuLog.SendRawPeci/") 7691da66f75SEd Tanous { 7701da66f75SEd Tanous entityPrivileges = { 7711da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 7721da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 7731da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 7741da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 7751da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 7761da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 7771da66f75SEd Tanous } 7781da66f75SEd Tanous 7791da66f75SEd Tanous private: 7801da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 7811da66f75SEd Tanous const std::vector<std::string> ¶ms) override 7821da66f75SEd Tanous { 783e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 7841da66f75SEd Tanous // Get the Raw PECI command from the request 785e1f26343SJason M. Bills nlohmann::json rawPECICmd; 786e1f26343SJason M. Bills if (!json_util::processJsonFromRequest(res, req, rawPECICmd)) 7871da66f75SEd Tanous { 7881da66f75SEd Tanous return; 7891da66f75SEd Tanous } 7901da66f75SEd Tanous // Get the Client Address from the request 791e1f26343SJason M. Bills nlohmann::json::const_iterator caIt = rawPECICmd.find("ClientAddress"); 792e1f26343SJason M. Bills if (caIt == rawPECICmd.end()) 7931da66f75SEd Tanous { 794*f12894f8SJason M. Bills messages::propertyMissing(asyncResp->res, "ClientAddress", 795*f12894f8SJason M. Bills "/ClientAddress"); 7961da66f75SEd Tanous return; 7971da66f75SEd Tanous } 7981da66f75SEd Tanous const uint64_t *ca = caIt->get_ptr<const uint64_t *>(); 7991da66f75SEd Tanous if (ca == nullptr) 8001da66f75SEd Tanous { 801*f12894f8SJason M. Bills messages::propertyValueTypeError(asyncResp->res, caIt->dump(), 802*f12894f8SJason M. Bills "ClientAddress", "/ClientAddress"); 8031da66f75SEd Tanous return; 8041da66f75SEd Tanous } 8051da66f75SEd Tanous // Get the Read Length from the request 8061da66f75SEd Tanous const uint8_t clientAddress = static_cast<uint8_t>(*ca); 807e1f26343SJason M. Bills nlohmann::json::const_iterator rlIt = rawPECICmd.find("ReadLength"); 808e1f26343SJason M. Bills if (rlIt == rawPECICmd.end()) 8091da66f75SEd Tanous { 810*f12894f8SJason M. Bills messages::propertyMissing(asyncResp->res, "ReadLength", 8111da66f75SEd Tanous "/ReadLength"); 8121da66f75SEd Tanous return; 8131da66f75SEd Tanous } 8141da66f75SEd Tanous const uint64_t *rl = rlIt->get_ptr<const uint64_t *>(); 8151da66f75SEd Tanous if (rl == nullptr) 8161da66f75SEd Tanous { 817*f12894f8SJason M. Bills messages::propertyValueTypeError(asyncResp->res, rlIt->dump(), 818*f12894f8SJason M. Bills "ReadLength", "/ReadLength"); 8191da66f75SEd Tanous return; 8201da66f75SEd Tanous } 8211da66f75SEd Tanous // Get the PECI Command from the request 8221da66f75SEd Tanous const uint32_t readLength = static_cast<uint32_t>(*rl); 823e1f26343SJason M. Bills nlohmann::json::const_iterator pcIt = rawPECICmd.find("PECICommand"); 824e1f26343SJason M. Bills if (pcIt == rawPECICmd.end()) 8251da66f75SEd Tanous { 826*f12894f8SJason M. Bills messages::propertyMissing(asyncResp->res, "PECICommand", 8271da66f75SEd Tanous "/PECICommand"); 8281da66f75SEd Tanous return; 8291da66f75SEd Tanous } 8301da66f75SEd Tanous std::vector<uint8_t> peciCommand; 8311da66f75SEd Tanous for (auto pc : *pcIt) 8321da66f75SEd Tanous { 8331da66f75SEd Tanous const uint64_t *val = pc.get_ptr<const uint64_t *>(); 8341da66f75SEd Tanous if (val == nullptr) 8351da66f75SEd Tanous { 8361da66f75SEd Tanous messages::propertyValueTypeError( 837*f12894f8SJason M. Bills asyncResp->res, pc.dump(), 838*f12894f8SJason M. Bills "PECICommand/" + std::to_string(peciCommand.size()), 8391da66f75SEd Tanous "/PECICommand"); 8401da66f75SEd Tanous return; 8411da66f75SEd Tanous } 8421da66f75SEd Tanous peciCommand.push_back(static_cast<uint8_t>(*val)); 8431da66f75SEd Tanous } 8441da66f75SEd Tanous // Callback to return the Raw PECI response 845e1f26343SJason M. Bills auto sendRawPECICallback = 846e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 8471da66f75SEd Tanous const std::vector<uint8_t> &resp) { 8481da66f75SEd Tanous if (ec) 8491da66f75SEd Tanous { 8501da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to send PECI command ec: " 8511da66f75SEd Tanous << ec.message(); 852*f12894f8SJason M. Bills messages::internalError(asyncResp->res); 8531da66f75SEd Tanous return; 8541da66f75SEd Tanous } 855e1f26343SJason M. Bills asyncResp->res.jsonValue = {{"Name", "PECI Command Response"}, 8561da66f75SEd Tanous {"PECIResponse", resp}}; 8571da66f75SEd Tanous }; 8581da66f75SEd Tanous // Call the SendRawPECI command with the provided data 8591da66f75SEd Tanous crow::connections::systemBus->async_method_call( 860e1f26343SJason M. Bills std::move(sendRawPECICallback), cpuLogObject, cpuLogPath, 861e1f26343SJason M. Bills cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength, 8624ed77cd5SEd Tanous peciCommand); 8631da66f75SEd Tanous } 8641da66f75SEd Tanous }; 8651da66f75SEd Tanous 8661da66f75SEd Tanous } // namespace redfish 867