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); 241*193ad2faSJason M. Bills static constexpr const long maxEntriesPerPage = 1000; 242*193ad2faSJason M. Bills long skip = 0; 243*193ad2faSJason M. Bills long top = maxEntriesPerPage; // Show max entries by default 244*193ad2faSJason M. Bills char *skipParam = req.urlParams.get("$skip"); 245*193ad2faSJason M. Bills if (skipParam != nullptr) 246*193ad2faSJason M. Bills { 247*193ad2faSJason M. Bills char *ptr = nullptr; 248*193ad2faSJason M. Bills skip = std::strtol(skipParam, &ptr, 10); 249*193ad2faSJason M. Bills if (*skipParam == '\0' || *ptr != '\0') 250*193ad2faSJason M. Bills { 251*193ad2faSJason M. Bills messages::addMessageToErrorJson( 252*193ad2faSJason M. Bills asyncResp->res.jsonValue, 253*193ad2faSJason M. Bills messages::queryParameterValueTypeError( 254*193ad2faSJason M. Bills std::string(skipParam), "$skip")); 255*193ad2faSJason M. Bills asyncResp->res.result(boost::beast::http::status::bad_request); 256*193ad2faSJason M. Bills return; 257*193ad2faSJason M. Bills } 258*193ad2faSJason M. Bills if (skip < 0) 259*193ad2faSJason M. Bills { 260*193ad2faSJason M. Bills messages::addMessageToErrorJson( 261*193ad2faSJason M. Bills asyncResp->res.jsonValue, 262*193ad2faSJason M. Bills messages::queryParameterOutOfRange( 263*193ad2faSJason M. Bills std::to_string(skip), "$skip", "greater than 0")); 264*193ad2faSJason M. Bills asyncResp->res.result(boost::beast::http::status::bad_request); 265*193ad2faSJason M. Bills return; 266*193ad2faSJason M. Bills } 267*193ad2faSJason M. Bills } 268*193ad2faSJason M. Bills char *topParam = req.urlParams.get("$top"); 269*193ad2faSJason M. Bills if (topParam != nullptr) 270*193ad2faSJason M. Bills { 271*193ad2faSJason M. Bills char *ptr = nullptr; 272*193ad2faSJason M. Bills top = std::strtol(topParam, &ptr, 10); 273*193ad2faSJason M. Bills if (*topParam == '\0' || *ptr != '\0') 274*193ad2faSJason M. Bills { 275*193ad2faSJason M. Bills messages::addMessageToErrorJson( 276*193ad2faSJason M. Bills asyncResp->res.jsonValue, 277*193ad2faSJason M. Bills messages::queryParameterValueTypeError( 278*193ad2faSJason M. Bills std::string(topParam), "$top")); 279*193ad2faSJason M. Bills asyncResp->res.result(boost::beast::http::status::bad_request); 280*193ad2faSJason M. Bills return; 281*193ad2faSJason M. Bills } 282*193ad2faSJason M. Bills if (top < 1 || top > maxEntriesPerPage) 283*193ad2faSJason M. Bills { 284*193ad2faSJason M. Bills messages::addMessageToErrorJson( 285*193ad2faSJason M. Bills asyncResp->res.jsonValue, 286*193ad2faSJason M. Bills messages::queryParameterOutOfRange( 287*193ad2faSJason M. Bills std::to_string(top), "$top", 288*193ad2faSJason M. Bills "1-" + std::to_string(maxEntriesPerPage))); 289*193ad2faSJason M. Bills asyncResp->res.result(boost::beast::http::status::bad_request); 290*193ad2faSJason M. Bills return; 291*193ad2faSJason M. Bills } 292*193ad2faSJason M. Bills } 293e1f26343SJason M. Bills // Collections don't include the static data added by SubRoute because 294e1f26343SJason M. Bills // it has a duplicate entry for members 295e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 296e1f26343SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 297e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 298e1f26343SJason M. Bills "/redfish/v1/" 299e1f26343SJason M. Bills "$metadata#LogEntryCollection.LogEntryCollection"; 300e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 301e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; 302e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; 303e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 304e1f26343SJason M. Bills "Collection of BMC Journal Entries"; 305e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 306e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 307e1f26343SJason M. Bills 308e1f26343SJason M. Bills // Go through the journal and use the timestamp to create a unique ID 309e1f26343SJason M. Bills // for each entry 310e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 311e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 312e1f26343SJason M. Bills if (ret < 0) 313e1f26343SJason M. Bills { 314e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 315f12894f8SJason M. Bills messages::internalError(asyncResp->res); 316e1f26343SJason M. Bills return; 317e1f26343SJason M. Bills } 318e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 319e1f26343SJason M. Bills journalTmp, sd_journal_close); 320e1f26343SJason M. Bills journalTmp = nullptr; 321e1f26343SJason M. Bills uint64_t prevTs = 0; 322e1f26343SJason M. Bills int index = 0; 323*193ad2faSJason M. Bills uint64_t entryCount = 0; 324e1f26343SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 325e1f26343SJason M. Bills { 326*193ad2faSJason M. Bills entryCount++; 327*193ad2faSJason M. Bills // Handle paging using skip (number of entries to skip from the 328*193ad2faSJason M. Bills // start) and top (number of entries to display) 329*193ad2faSJason M. Bills if (entryCount <= skip || entryCount > skip + top) 330*193ad2faSJason M. Bills { 331*193ad2faSJason M. Bills continue; 332*193ad2faSJason M. Bills } 333*193ad2faSJason M. Bills 334e1f26343SJason M. Bills // Get the entry timestamp 335e1f26343SJason M. Bills uint64_t curTs = 0; 336e1f26343SJason M. Bills ret = sd_journal_get_realtime_usec(journal.get(), &curTs); 337e1f26343SJason M. Bills if (ret < 0) 338e1f26343SJason M. Bills { 339e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 340e1f26343SJason M. Bills << strerror(-ret); 341e1f26343SJason M. Bills continue; 342e1f26343SJason M. Bills } 343e1f26343SJason M. Bills // If the timestamp isn't unique, increment the index 344e1f26343SJason M. Bills if (curTs == prevTs) 345e1f26343SJason M. Bills { 346e1f26343SJason M. Bills index++; 347e1f26343SJason M. Bills } 348e1f26343SJason M. Bills else 349e1f26343SJason M. Bills { 350e1f26343SJason M. Bills // Otherwise, reset it 351e1f26343SJason M. Bills index = 0; 352e1f26343SJason M. Bills } 353e1f26343SJason M. Bills // Save the timestamp 354e1f26343SJason M. Bills prevTs = curTs; 355e1f26343SJason M. Bills 356e1f26343SJason M. Bills std::string idStr(std::to_string(curTs)); 357e1f26343SJason M. Bills if (index > 0) 358e1f26343SJason M. Bills { 359e1f26343SJason M. Bills idStr += "_" + std::to_string(index); 360e1f26343SJason M. Bills } 361e1f26343SJason M. Bills logEntryArray.push_back({}); 362e1f26343SJason M. Bills nlohmann::json &bmcLogEntry = logEntryArray.back(); 363e1f26343SJason M. Bills if (fillBMCLogEntryJson(idStr, journal.get(), bmcLogEntry) != 0) 364e1f26343SJason M. Bills { 365f12894f8SJason M. Bills messages::internalError(asyncResp->res); 366e1f26343SJason M. Bills return; 367e1f26343SJason M. Bills } 368e1f26343SJason M. Bills } 369*193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 370*193ad2faSJason M. Bills if (skip + top < entryCount) 371*193ad2faSJason M. Bills { 372*193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 373*193ad2faSJason M. Bills "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" + 374*193ad2faSJason M. Bills std::to_string(skip + top); 375*193ad2faSJason M. Bills } 376e1f26343SJason M. Bills } 377e1f26343SJason M. Bills }; 378e1f26343SJason M. Bills 379e1f26343SJason M. Bills class BMCLogEntry : public Node 380e1f26343SJason M. Bills { 381e1f26343SJason M. Bills public: 382e1f26343SJason M. Bills BMCLogEntry(CrowApp &app) : 383e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/<str>/", 384e1f26343SJason M. Bills std::string()) 385e1f26343SJason M. Bills { 386e1f26343SJason M. Bills entityPrivileges = { 387e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 388e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 389e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 390e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 391e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 392e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 393e1f26343SJason M. Bills } 394e1f26343SJason M. Bills 395e1f26343SJason M. Bills private: 396e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 397e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 398e1f26343SJason M. Bills { 399e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 400e1f26343SJason M. Bills if (params.size() != 1) 401e1f26343SJason M. Bills { 402f12894f8SJason M. Bills messages::internalError(asyncResp->res); 403e1f26343SJason M. Bills return; 404e1f26343SJason M. Bills } 405e1f26343SJason M. Bills // Convert the unique ID back to a timestamp to find the entry 406e1f26343SJason M. Bills boost::string_view tsStr(params[0]); 407e1f26343SJason M. Bills boost::string_view indexStr(params[0]); 408e1f26343SJason M. Bills uint64_t ts = 0; 409e1f26343SJason M. Bills uint16_t index = 0; 410e1f26343SJason M. Bills auto underscorePos = tsStr.find("_"); 411e1f26343SJason M. Bills if (underscorePos == tsStr.npos) 412e1f26343SJason M. Bills { 413e1f26343SJason M. Bills // Timestamp has no index 414e1f26343SJason M. Bills ts = strtoull(tsStr.data(), nullptr, 10); 415e1f26343SJason M. Bills } 416e1f26343SJason M. Bills else 417e1f26343SJason M. Bills { 418e1f26343SJason M. Bills // Timestamp has an index 419e1f26343SJason M. Bills tsStr.remove_suffix(tsStr.size() - underscorePos + 1); 420e1f26343SJason M. Bills ts = strtoull(tsStr.data(), nullptr, 10); 421e1f26343SJason M. Bills indexStr.remove_prefix(underscorePos + 1); 422e1f26343SJason M. Bills index = 423e1f26343SJason M. Bills static_cast<uint16_t>(strtoul(indexStr.data(), nullptr, 10)); 424e1f26343SJason M. Bills } 425e1f26343SJason M. Bills 426e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 427e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 428e1f26343SJason M. Bills if (ret < 0) 429e1f26343SJason M. Bills { 430e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 431f12894f8SJason M. Bills messages::internalError(asyncResp->res); 432e1f26343SJason M. Bills return; 433e1f26343SJason M. Bills } 434e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 435e1f26343SJason M. Bills journalTmp, sd_journal_close); 436e1f26343SJason M. Bills journalTmp = nullptr; 437e1f26343SJason M. Bills // Go to the timestamp in the log and move to the entry at the index 438e1f26343SJason M. Bills ret = sd_journal_seek_realtime_usec(journal.get(), ts); 439e1f26343SJason M. Bills for (int i = 0; i <= index; i++) 440e1f26343SJason M. Bills { 441e1f26343SJason M. Bills sd_journal_next(journal.get()); 442e1f26343SJason M. Bills } 443e1f26343SJason M. Bills if (fillBMCLogEntryJson(params[0], journal.get(), 444e1f26343SJason M. Bills asyncResp->res.jsonValue) != 0) 445e1f26343SJason M. Bills { 446f12894f8SJason M. Bills messages::internalError(asyncResp->res); 447e1f26343SJason M. Bills return; 448e1f26343SJason M. Bills } 449e1f26343SJason M. Bills } 450e1f26343SJason M. Bills }; 451e1f26343SJason M. Bills 452e1f26343SJason M. Bills class CPULogService : public Node 453e1f26343SJason M. Bills { 454e1f26343SJason M. Bills public: 455e1f26343SJason M. Bills template <typename CrowApp> 456e1f26343SJason M. Bills CPULogService(CrowApp &app) : 457e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/") 4581da66f75SEd Tanous { 4591da66f75SEd Tanous // Set the id for SubRoute 4604ed77cd5SEd Tanous Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/CpuLog"; 4611da66f75SEd Tanous entityPrivileges = { 462e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 463e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 464e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 465e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 466e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 467e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 4681da66f75SEd Tanous } 4691da66f75SEd Tanous 4701da66f75SEd Tanous private: 4711da66f75SEd Tanous /** 4721da66f75SEd Tanous * Functions triggers appropriate requests on DBus 4731da66f75SEd Tanous */ 4741da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 4751da66f75SEd Tanous const std::vector<std::string> ¶ms) override 4761da66f75SEd Tanous { 477e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 4781da66f75SEd Tanous // Copy over the static data to include the entries added by SubRoute 479e1f26343SJason M. Bills asyncResp->res.jsonValue = Node::json; 480e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 481e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 482e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 483e1f26343SJason M. Bills "/redfish/v1/" 4841da66f75SEd Tanous "$metadata#LogService.LogService"; 485e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service"; 486e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = "CPU Log Service"; 487e1f26343SJason M. Bills asyncResp->res.jsonValue["Id"] = "CPU Log"; 488e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 489e1f26343SJason M. Bills asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 490e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"] = { 4911da66f75SEd Tanous {"Oem", 4921da66f75SEd Tanous {{"#CpuLog.Immediate", 4931da66f75SEd Tanous {{"target", 4944ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 4951da66f75SEd Tanous "CpuLog.Immediate"}}}}}}; 4961da66f75SEd Tanous 4971da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI 498e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"]["Oem"].push_back( 4991da66f75SEd Tanous {"#CpuLog.SendRawPeci", 5001da66f75SEd Tanous {{"target", 5014ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 5021da66f75SEd Tanous "CpuLog.SendRawPeci"}}}); 5031da66f75SEd Tanous #endif 5041da66f75SEd Tanous } 5051da66f75SEd Tanous }; 5061da66f75SEd Tanous 507e1f26343SJason M. Bills class CPULogEntryCollection : public Node 5081da66f75SEd Tanous { 5091da66f75SEd Tanous public: 5101da66f75SEd Tanous template <typename CrowApp> 511e1f26343SJason M. Bills CPULogEntryCollection(CrowApp &app) : 512e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/") 5131da66f75SEd Tanous { 5141da66f75SEd Tanous // Collections use static ID for SubRoute to add to its parent, but only 5151da66f75SEd Tanous // load dynamic data so the duplicate static members don't get displayed 5161da66f75SEd Tanous Node::json["@odata.id"] = 5174ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries"; 5181da66f75SEd Tanous entityPrivileges = { 519e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 520e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 521e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 522e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 523e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 524e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 5251da66f75SEd Tanous } 5261da66f75SEd Tanous 5271da66f75SEd Tanous private: 5281da66f75SEd Tanous /** 5291da66f75SEd Tanous * Functions triggers appropriate requests on DBus 5301da66f75SEd Tanous */ 5311da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 5321da66f75SEd Tanous const std::vector<std::string> ¶ms) override 5331da66f75SEd Tanous { 534e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 5351da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 5361da66f75SEd Tanous // it has a duplicate entry for members 537e1f26343SJason M. Bills auto getLogEntriesCallback = [asyncResp]( 538e1f26343SJason M. Bills const boost::system::error_code ec, 5391da66f75SEd Tanous const std::vector<std::string> &resp) { 5401da66f75SEd Tanous if (ec) 5411da66f75SEd Tanous { 5421da66f75SEd Tanous if (ec.value() != 5431da66f75SEd Tanous boost::system::errc::no_such_file_or_directory) 5441da66f75SEd Tanous { 5451da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to get entries ec: " 5461da66f75SEd Tanous << ec.message(); 547f12894f8SJason M. Bills messages::internalError(asyncResp->res); 5481da66f75SEd Tanous return; 5491da66f75SEd Tanous } 5501da66f75SEd Tanous } 551e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 5521da66f75SEd Tanous "#LogEntryCollection.LogEntryCollection"; 553e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 5541da66f75SEd Tanous "/redfish/v1/" 5551da66f75SEd Tanous "$metadata#LogEntryCollection.LogEntryCollection"; 556e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 557e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries"; 558e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries"; 559e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 560e1f26343SJason M. Bills "Collection of CPU Log Entries"; 561e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 562e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 5631da66f75SEd Tanous for (const std::string &objpath : resp) 5641da66f75SEd Tanous { 5651da66f75SEd Tanous // Don't list the immediate log 5664ed77cd5SEd Tanous if (objpath.compare(cpuLogImmediatePath) == 0) 5671da66f75SEd Tanous { 5681da66f75SEd Tanous continue; 5691da66f75SEd Tanous } 5704ed77cd5SEd Tanous std::size_t lastPos = objpath.rfind("/"); 5714ed77cd5SEd Tanous if (lastPos != std::string::npos) 5721da66f75SEd Tanous { 573e1f26343SJason M. Bills logEntryArray.push_back( 574e1f26343SJason M. Bills {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/" 575e1f26343SJason M. Bills "CpuLog/Entries/" + 5764ed77cd5SEd Tanous objpath.substr(lastPos + 1)}}); 5771da66f75SEd Tanous } 5781da66f75SEd Tanous } 579e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 580e1f26343SJason M. Bills logEntryArray.size(); 5811da66f75SEd Tanous }; 5821da66f75SEd Tanous crow::connections::systemBus->async_method_call( 5831da66f75SEd Tanous std::move(getLogEntriesCallback), 5841da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", 5851da66f75SEd Tanous "/xyz/openbmc_project/object_mapper", 5861da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0, 5874ed77cd5SEd Tanous std::array<const char *, 1>{cpuLogInterface}); 5881da66f75SEd Tanous } 5891da66f75SEd Tanous }; 5901da66f75SEd Tanous 5911da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog) 5921da66f75SEd Tanous { 5931da66f75SEd Tanous nlohmann::json::const_iterator metaIt = cpuLog.find("metadata"); 5941da66f75SEd Tanous if (metaIt != cpuLog.end()) 5951da66f75SEd Tanous { 5961da66f75SEd Tanous nlohmann::json::const_iterator tsIt = metaIt->find("timestamp"); 5971da66f75SEd Tanous if (tsIt != metaIt->end()) 5981da66f75SEd Tanous { 5991da66f75SEd Tanous const std::string *logTime = tsIt->get_ptr<const std::string *>(); 6001da66f75SEd Tanous if (logTime != nullptr) 6011da66f75SEd Tanous { 6021da66f75SEd Tanous return *logTime; 6031da66f75SEd Tanous } 6041da66f75SEd Tanous } 6051da66f75SEd Tanous } 6061da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to find log timestamp"; 6071da66f75SEd Tanous 6081da66f75SEd Tanous return std::string(); 6091da66f75SEd Tanous } 6101da66f75SEd Tanous 611e1f26343SJason M. Bills class CPULogEntry : public Node 6121da66f75SEd Tanous { 6131da66f75SEd Tanous public: 614e1f26343SJason M. Bills CPULogEntry(CrowApp &app) : 6154ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/", 6161da66f75SEd Tanous std::string()) 6171da66f75SEd Tanous { 6181da66f75SEd Tanous entityPrivileges = { 619e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 620e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 621e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 622e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 623e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 624e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 6251da66f75SEd Tanous } 6261da66f75SEd Tanous 6271da66f75SEd Tanous private: 6281da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 6291da66f75SEd Tanous const std::vector<std::string> ¶ms) override 6301da66f75SEd Tanous { 631e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 6321da66f75SEd Tanous if (params.size() != 1) 6331da66f75SEd Tanous { 634f12894f8SJason M. Bills messages::internalError(asyncResp->res); 6351da66f75SEd Tanous return; 6361da66f75SEd Tanous } 6374ed77cd5SEd Tanous const uint8_t logId = std::atoi(params[0].c_str()); 638e1f26343SJason M. Bills auto getStoredLogCallback = 639e1f26343SJason M. Bills [asyncResp, 6404ed77cd5SEd Tanous logId](const boost::system::error_code ec, 641e1f26343SJason M. Bills const sdbusplus::message::variant<std::string> &resp) { 6421da66f75SEd Tanous if (ec) 6431da66f75SEd Tanous { 644e1f26343SJason M. Bills BMCWEB_LOG_DEBUG << "failed to get log ec: " 645e1f26343SJason M. Bills << ec.message(); 646f12894f8SJason M. Bills messages::internalError(asyncResp->res); 6471da66f75SEd Tanous return; 6481da66f75SEd Tanous } 649e1f26343SJason M. Bills const std::string *log = 650e1f26343SJason M. Bills mapbox::getPtr<const std::string>(resp); 6511da66f75SEd Tanous if (log == nullptr) 6521da66f75SEd Tanous { 653f12894f8SJason M. Bills messages::internalError(asyncResp->res); 6541da66f75SEd Tanous return; 6551da66f75SEd Tanous } 6561da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 6571da66f75SEd Tanous if (j.is_discarded()) 6581da66f75SEd Tanous { 659f12894f8SJason M. Bills messages::internalError(asyncResp->res); 6601da66f75SEd Tanous return; 6611da66f75SEd Tanous } 6621da66f75SEd Tanous std::string t = getLogCreatedTime(j); 663e1f26343SJason M. Bills asyncResp->res.jsonValue = { 6641da66f75SEd Tanous {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 665e1f26343SJason M. Bills {"@odata.context", 666e1f26343SJason M. Bills "/redfish/v1/$metadata#LogEntry.LogEntry"}, 6671da66f75SEd Tanous {"@odata.id", 6684ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" + 6694ed77cd5SEd Tanous std::to_string(logId)}, 6701da66f75SEd Tanous {"Name", "CPU Debug Log"}, 6714ed77cd5SEd Tanous {"Id", logId}, 6721da66f75SEd Tanous {"EntryType", "Oem"}, 6731da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 6741da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 6751da66f75SEd Tanous {"Created", std::move(t)}}; 6761da66f75SEd Tanous }; 6771da66f75SEd Tanous crow::connections::systemBus->async_method_call( 6784ed77cd5SEd Tanous std::move(getStoredLogCallback), cpuLogObject, 6794ed77cd5SEd Tanous cpuLogPath + std::string("/") + std::to_string(logId), 6804ed77cd5SEd Tanous "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log"); 6811da66f75SEd Tanous } 6821da66f75SEd Tanous }; 6831da66f75SEd Tanous 684e1f26343SJason M. Bills class ImmediateCPULog : public Node 6851da66f75SEd Tanous { 6861da66f75SEd Tanous public: 687e1f26343SJason M. Bills ImmediateCPULog(CrowApp &app) : 6884ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 689e1f26343SJason M. Bills "CpuLog.Immediate/") 6901da66f75SEd Tanous { 6911da66f75SEd Tanous entityPrivileges = { 692e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 693e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 694e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 695e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 696e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 697e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 6981da66f75SEd Tanous } 6991da66f75SEd Tanous 7001da66f75SEd Tanous private: 7011da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 7021da66f75SEd Tanous const std::vector<std::string> ¶ms) override 7031da66f75SEd Tanous { 704e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 7051da66f75SEd Tanous static std::unique_ptr<sdbusplus::bus::match::match> 7061da66f75SEd Tanous immediateLogMatcher; 7071da66f75SEd Tanous 7081da66f75SEd Tanous // Only allow one Immediate Log request at a time 7091da66f75SEd Tanous if (immediateLogMatcher != nullptr) 7101da66f75SEd Tanous { 711e1f26343SJason M. Bills asyncResp->res.addHeader("Retry-After", "30"); 712f12894f8SJason M. Bills messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); 7131da66f75SEd Tanous return; 7141da66f75SEd Tanous } 7151da66f75SEd Tanous // Make this static so it survives outside this method 7161da66f75SEd Tanous static boost::asio::deadline_timer timeout(*req.ioService); 7171da66f75SEd Tanous 7181da66f75SEd Tanous timeout.expires_from_now(boost::posix_time::seconds(30)); 719e1f26343SJason M. Bills timeout.async_wait([asyncResp](const boost::system::error_code &ec) { 7201da66f75SEd Tanous immediateLogMatcher = nullptr; 7211da66f75SEd Tanous if (ec) 7221da66f75SEd Tanous { 7231da66f75SEd Tanous // operation_aborted is expected if timer is canceled before 7241da66f75SEd Tanous // completion. 7251da66f75SEd Tanous if (ec != boost::asio::error::operation_aborted) 7261da66f75SEd Tanous { 7271da66f75SEd Tanous BMCWEB_LOG_ERROR << "Async_wait failed " << ec; 7281da66f75SEd Tanous } 7291da66f75SEd Tanous return; 7301da66f75SEd Tanous } 7311da66f75SEd Tanous BMCWEB_LOG_ERROR << "Timed out waiting for immediate log"; 7321da66f75SEd Tanous 733f12894f8SJason M. Bills messages::internalError(asyncResp->res); 7341da66f75SEd Tanous }); 7351da66f75SEd Tanous 736e1f26343SJason M. Bills auto immediateLogMatcherCallback = [asyncResp]( 7371da66f75SEd Tanous sdbusplus::message::message &m) { 7381da66f75SEd Tanous BMCWEB_LOG_DEBUG << "Immediate log available match fired"; 7391da66f75SEd Tanous boost::system::error_code ec; 7401da66f75SEd Tanous timeout.cancel(ec); 7411da66f75SEd Tanous if (ec) 7421da66f75SEd Tanous { 7431da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " << ec; 7441da66f75SEd Tanous } 7454ed77cd5SEd Tanous sdbusplus::message::object_path objPath; 7461da66f75SEd Tanous boost::container::flat_map< 7471da66f75SEd Tanous std::string, 7481da66f75SEd Tanous boost::container::flat_map< 7491da66f75SEd Tanous std::string, sdbusplus::message::variant<std::string>>> 7504ed77cd5SEd Tanous interfacesAdded; 7514ed77cd5SEd Tanous m.read(objPath, interfacesAdded); 7521da66f75SEd Tanous const std::string *log = mapbox::getPtr<const std::string>( 7534ed77cd5SEd Tanous interfacesAdded[cpuLogInterface]["Log"]); 7541da66f75SEd Tanous if (log == nullptr) 7551da66f75SEd Tanous { 756f12894f8SJason M. Bills messages::internalError(asyncResp->res); 7571da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 7581da66f75SEd Tanous // match object inside which this lambda is executing. Once it 7591da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 7601da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 7611da66f75SEd Tanous // be the last thing done. 7621da66f75SEd Tanous immediateLogMatcher = nullptr; 7631da66f75SEd Tanous return; 7641da66f75SEd Tanous } 7651da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 7661da66f75SEd Tanous if (j.is_discarded()) 7671da66f75SEd Tanous { 768f12894f8SJason M. Bills messages::internalError(asyncResp->res); 7691da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 7701da66f75SEd Tanous // match object inside which this lambda is executing. Once it 7711da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 7721da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 7731da66f75SEd Tanous // be the last thing done. 7741da66f75SEd Tanous immediateLogMatcher = nullptr; 7751da66f75SEd Tanous return; 7761da66f75SEd Tanous } 7771da66f75SEd Tanous std::string t = getLogCreatedTime(j); 778e1f26343SJason M. Bills asyncResp->res.jsonValue = { 7791da66f75SEd Tanous {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 7801da66f75SEd Tanous {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 7811da66f75SEd Tanous {"Name", "CPU Debug Log"}, 7821da66f75SEd Tanous {"EntryType", "Oem"}, 7831da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 7841da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 7851da66f75SEd Tanous {"Created", std::move(t)}}; 7861da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 7871da66f75SEd Tanous // match object inside which this lambda is executing. Once it is 7881da66f75SEd Tanous // set to nullptr, the match object will be destroyed and the lambda 7891da66f75SEd Tanous // will lose its context, including res, so it needs to be the last 7901da66f75SEd Tanous // thing done. 7911da66f75SEd Tanous immediateLogMatcher = nullptr; 7921da66f75SEd Tanous }; 7931da66f75SEd Tanous immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>( 7941da66f75SEd Tanous *crow::connections::systemBus, 7951da66f75SEd Tanous sdbusplus::bus::match::rules::interfacesAdded() + 7964ed77cd5SEd Tanous sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath), 7971da66f75SEd Tanous std::move(immediateLogMatcherCallback)); 7981da66f75SEd Tanous 7991da66f75SEd Tanous auto generateImmediateLogCallback = 800e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 8011da66f75SEd Tanous const std::string &resp) { 8021da66f75SEd Tanous if (ec) 8031da66f75SEd Tanous { 8041da66f75SEd Tanous if (ec.value() == 8051da66f75SEd Tanous boost::system::errc::operation_not_supported) 8061da66f75SEd Tanous { 807f12894f8SJason M. Bills messages::resourceInStandby(asyncResp->res); 8081da66f75SEd Tanous } 8091da66f75SEd Tanous else 8101da66f75SEd Tanous { 811f12894f8SJason M. Bills messages::internalError(asyncResp->res); 8121da66f75SEd Tanous } 8131da66f75SEd Tanous boost::system::error_code timeoutec; 8141da66f75SEd Tanous timeout.cancel(timeoutec); 8151da66f75SEd Tanous if (timeoutec) 8161da66f75SEd Tanous { 8171da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " 8181da66f75SEd Tanous << timeoutec; 8191da66f75SEd Tanous } 8201da66f75SEd Tanous immediateLogMatcher = nullptr; 8211da66f75SEd Tanous return; 8221da66f75SEd Tanous } 8231da66f75SEd Tanous }; 8241da66f75SEd Tanous crow::connections::systemBus->async_method_call( 8254ed77cd5SEd Tanous std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath, 8264ed77cd5SEd Tanous cpuLogImmediateInterface, "GenerateImmediateLog"); 8271da66f75SEd Tanous } 8281da66f75SEd Tanous }; 8291da66f75SEd Tanous 830e1f26343SJason M. Bills class SendRawPECI : public Node 8311da66f75SEd Tanous { 8321da66f75SEd Tanous public: 833e1f26343SJason M. Bills SendRawPECI(CrowApp &app) : 8344ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 835e1f26343SJason M. Bills "CpuLog.SendRawPeci/") 8361da66f75SEd Tanous { 8371da66f75SEd Tanous entityPrivileges = { 8381da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 8391da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 8401da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 8411da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 8421da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 8431da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 8441da66f75SEd Tanous } 8451da66f75SEd Tanous 8461da66f75SEd Tanous private: 8471da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 8481da66f75SEd Tanous const std::vector<std::string> ¶ms) override 8491da66f75SEd Tanous { 850e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 8511da66f75SEd Tanous // Get the Raw PECI command from the request 852e1f26343SJason M. Bills nlohmann::json rawPECICmd; 853e1f26343SJason M. Bills if (!json_util::processJsonFromRequest(res, req, rawPECICmd)) 8541da66f75SEd Tanous { 8551da66f75SEd Tanous return; 8561da66f75SEd Tanous } 8571da66f75SEd Tanous // Get the Client Address from the request 858e1f26343SJason M. Bills nlohmann::json::const_iterator caIt = rawPECICmd.find("ClientAddress"); 859e1f26343SJason M. Bills if (caIt == rawPECICmd.end()) 8601da66f75SEd Tanous { 861f12894f8SJason M. Bills messages::propertyMissing(asyncResp->res, "ClientAddress", 862f12894f8SJason M. Bills "/ClientAddress"); 8631da66f75SEd Tanous return; 8641da66f75SEd Tanous } 8651da66f75SEd Tanous const uint64_t *ca = caIt->get_ptr<const uint64_t *>(); 8661da66f75SEd Tanous if (ca == nullptr) 8671da66f75SEd Tanous { 868f12894f8SJason M. Bills messages::propertyValueTypeError(asyncResp->res, caIt->dump(), 869f12894f8SJason M. Bills "ClientAddress", "/ClientAddress"); 8701da66f75SEd Tanous return; 8711da66f75SEd Tanous } 8721da66f75SEd Tanous // Get the Read Length from the request 8731da66f75SEd Tanous const uint8_t clientAddress = static_cast<uint8_t>(*ca); 874e1f26343SJason M. Bills nlohmann::json::const_iterator rlIt = rawPECICmd.find("ReadLength"); 875e1f26343SJason M. Bills if (rlIt == rawPECICmd.end()) 8761da66f75SEd Tanous { 877f12894f8SJason M. Bills messages::propertyMissing(asyncResp->res, "ReadLength", 8781da66f75SEd Tanous "/ReadLength"); 8791da66f75SEd Tanous return; 8801da66f75SEd Tanous } 8811da66f75SEd Tanous const uint64_t *rl = rlIt->get_ptr<const uint64_t *>(); 8821da66f75SEd Tanous if (rl == nullptr) 8831da66f75SEd Tanous { 884f12894f8SJason M. Bills messages::propertyValueTypeError(asyncResp->res, rlIt->dump(), 885f12894f8SJason M. Bills "ReadLength", "/ReadLength"); 8861da66f75SEd Tanous return; 8871da66f75SEd Tanous } 8881da66f75SEd Tanous // Get the PECI Command from the request 8891da66f75SEd Tanous const uint32_t readLength = static_cast<uint32_t>(*rl); 890e1f26343SJason M. Bills nlohmann::json::const_iterator pcIt = rawPECICmd.find("PECICommand"); 891e1f26343SJason M. Bills if (pcIt == rawPECICmd.end()) 8921da66f75SEd Tanous { 893f12894f8SJason M. Bills messages::propertyMissing(asyncResp->res, "PECICommand", 8941da66f75SEd Tanous "/PECICommand"); 8951da66f75SEd Tanous return; 8961da66f75SEd Tanous } 8971da66f75SEd Tanous std::vector<uint8_t> peciCommand; 8981da66f75SEd Tanous for (auto pc : *pcIt) 8991da66f75SEd Tanous { 9001da66f75SEd Tanous const uint64_t *val = pc.get_ptr<const uint64_t *>(); 9011da66f75SEd Tanous if (val == nullptr) 9021da66f75SEd Tanous { 9031da66f75SEd Tanous messages::propertyValueTypeError( 904f12894f8SJason M. Bills asyncResp->res, pc.dump(), 905f12894f8SJason M. Bills "PECICommand/" + std::to_string(peciCommand.size()), 9061da66f75SEd Tanous "/PECICommand"); 9071da66f75SEd Tanous return; 9081da66f75SEd Tanous } 9091da66f75SEd Tanous peciCommand.push_back(static_cast<uint8_t>(*val)); 9101da66f75SEd Tanous } 9111da66f75SEd Tanous // Callback to return the Raw PECI response 912e1f26343SJason M. Bills auto sendRawPECICallback = 913e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 9141da66f75SEd Tanous const std::vector<uint8_t> &resp) { 9151da66f75SEd Tanous if (ec) 9161da66f75SEd Tanous { 9171da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to send PECI command ec: " 9181da66f75SEd Tanous << ec.message(); 919f12894f8SJason M. Bills messages::internalError(asyncResp->res); 9201da66f75SEd Tanous return; 9211da66f75SEd Tanous } 922e1f26343SJason M. Bills asyncResp->res.jsonValue = {{"Name", "PECI Command Response"}, 9231da66f75SEd Tanous {"PECIResponse", resp}}; 9241da66f75SEd Tanous }; 9251da66f75SEd Tanous // Call the SendRawPECI command with the provided data 9261da66f75SEd Tanous crow::connections::systemBus->async_method_call( 927e1f26343SJason M. Bills std::move(sendRawPECICallback), cpuLogObject, cpuLogPath, 928e1f26343SJason M. Bills cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength, 9294ed77cd5SEd Tanous peciCommand); 9301da66f75SEd Tanous } 9311da66f75SEd Tanous }; 9321da66f75SEd Tanous 9331da66f75SEd Tanous } // namespace redfish 934