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); 241193ad2faSJason M. Bills static constexpr const long maxEntriesPerPage = 1000; 242193ad2faSJason M. Bills long skip = 0; 243193ad2faSJason M. Bills long top = maxEntriesPerPage; // Show max entries by default 244193ad2faSJason M. Bills char *skipParam = req.urlParams.get("$skip"); 245193ad2faSJason M. Bills if (skipParam != nullptr) 246193ad2faSJason M. Bills { 247193ad2faSJason M. Bills char *ptr = nullptr; 248193ad2faSJason M. Bills skip = std::strtol(skipParam, &ptr, 10); 249193ad2faSJason M. Bills if (*skipParam == '\0' || *ptr != '\0') 250193ad2faSJason M. Bills { 251*35a62c7cSJason M. Bills 252193ad2faSJason M. Bills messages::queryParameterValueTypeError( 253*35a62c7cSJason M. Bills asyncResp->res, std::string(skipParam), "$skip"); 254193ad2faSJason M. Bills return; 255193ad2faSJason M. Bills } 256193ad2faSJason M. Bills if (skip < 0) 257193ad2faSJason M. Bills { 258*35a62c7cSJason M. Bills 259*35a62c7cSJason M. Bills messages::queryParameterOutOfRange(asyncResp->res, 260*35a62c7cSJason M. Bills std::to_string(skip), 261*35a62c7cSJason M. Bills "$skip", "greater than 0"); 262193ad2faSJason M. Bills return; 263193ad2faSJason M. Bills } 264193ad2faSJason M. Bills } 265193ad2faSJason M. Bills char *topParam = req.urlParams.get("$top"); 266193ad2faSJason M. Bills if (topParam != nullptr) 267193ad2faSJason M. Bills { 268193ad2faSJason M. Bills char *ptr = nullptr; 269193ad2faSJason M. Bills top = std::strtol(topParam, &ptr, 10); 270193ad2faSJason M. Bills if (*topParam == '\0' || *ptr != '\0') 271193ad2faSJason M. Bills { 272193ad2faSJason M. Bills messages::queryParameterValueTypeError( 273*35a62c7cSJason M. Bills asyncResp->res, std::string(topParam), "$top"); 274193ad2faSJason M. Bills return; 275193ad2faSJason M. Bills } 276193ad2faSJason M. Bills if (top < 1 || top > maxEntriesPerPage) 277193ad2faSJason M. Bills { 278*35a62c7cSJason M. Bills 279193ad2faSJason M. Bills messages::queryParameterOutOfRange( 280*35a62c7cSJason M. Bills asyncResp->res, std::to_string(top), "$top", 281*35a62c7cSJason M. Bills "1-" + std::to_string(maxEntriesPerPage)); 282193ad2faSJason M. Bills asyncResp->res.result(boost::beast::http::status::bad_request); 283193ad2faSJason M. Bills return; 284193ad2faSJason M. Bills } 285193ad2faSJason M. Bills } 286e1f26343SJason M. Bills // Collections don't include the static data added by SubRoute because 287e1f26343SJason M. Bills // it has a duplicate entry for members 288e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 289e1f26343SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 290e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 291e1f26343SJason M. Bills "/redfish/v1/" 292e1f26343SJason M. Bills "$metadata#LogEntryCollection.LogEntryCollection"; 293e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 294e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; 295e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; 296e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 297e1f26343SJason M. Bills "Collection of BMC Journal Entries"; 298e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 299e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 300e1f26343SJason M. Bills 301e1f26343SJason M. Bills // Go through the journal and use the timestamp to create a unique ID 302e1f26343SJason M. Bills // for each entry 303e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 304e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 305e1f26343SJason M. Bills if (ret < 0) 306e1f26343SJason M. Bills { 307e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 308f12894f8SJason M. Bills messages::internalError(asyncResp->res); 309e1f26343SJason M. Bills return; 310e1f26343SJason M. Bills } 311e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 312e1f26343SJason M. Bills journalTmp, sd_journal_close); 313e1f26343SJason M. Bills journalTmp = nullptr; 314e1f26343SJason M. Bills uint64_t prevTs = 0; 315e1f26343SJason M. Bills int index = 0; 316193ad2faSJason M. Bills uint64_t entryCount = 0; 317e1f26343SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 318e1f26343SJason M. Bills { 319193ad2faSJason M. Bills entryCount++; 320193ad2faSJason M. Bills // Handle paging using skip (number of entries to skip from the 321193ad2faSJason M. Bills // start) and top (number of entries to display) 322193ad2faSJason M. Bills if (entryCount <= skip || entryCount > skip + top) 323193ad2faSJason M. Bills { 324193ad2faSJason M. Bills continue; 325193ad2faSJason M. Bills } 326193ad2faSJason M. Bills 327e1f26343SJason M. Bills // Get the entry timestamp 328e1f26343SJason M. Bills uint64_t curTs = 0; 329e1f26343SJason M. Bills ret = sd_journal_get_realtime_usec(journal.get(), &curTs); 330e1f26343SJason M. Bills if (ret < 0) 331e1f26343SJason M. Bills { 332e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 333e1f26343SJason M. Bills << strerror(-ret); 334e1f26343SJason M. Bills continue; 335e1f26343SJason M. Bills } 336e1f26343SJason M. Bills // If the timestamp isn't unique, increment the index 337e1f26343SJason M. Bills if (curTs == prevTs) 338e1f26343SJason M. Bills { 339e1f26343SJason M. Bills index++; 340e1f26343SJason M. Bills } 341e1f26343SJason M. Bills else 342e1f26343SJason M. Bills { 343e1f26343SJason M. Bills // Otherwise, reset it 344e1f26343SJason M. Bills index = 0; 345e1f26343SJason M. Bills } 346e1f26343SJason M. Bills // Save the timestamp 347e1f26343SJason M. Bills prevTs = curTs; 348e1f26343SJason M. Bills 349e1f26343SJason M. Bills std::string idStr(std::to_string(curTs)); 350e1f26343SJason M. Bills if (index > 0) 351e1f26343SJason M. Bills { 352e1f26343SJason M. Bills idStr += "_" + std::to_string(index); 353e1f26343SJason M. Bills } 354e1f26343SJason M. Bills logEntryArray.push_back({}); 355e1f26343SJason M. Bills nlohmann::json &bmcLogEntry = logEntryArray.back(); 356e1f26343SJason M. Bills if (fillBMCLogEntryJson(idStr, journal.get(), bmcLogEntry) != 0) 357e1f26343SJason M. Bills { 358f12894f8SJason M. Bills messages::internalError(asyncResp->res); 359e1f26343SJason M. Bills return; 360e1f26343SJason M. Bills } 361e1f26343SJason M. Bills } 362193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 363193ad2faSJason M. Bills if (skip + top < entryCount) 364193ad2faSJason M. Bills { 365193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 366193ad2faSJason M. Bills "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" + 367193ad2faSJason M. Bills std::to_string(skip + top); 368193ad2faSJason M. Bills } 369e1f26343SJason M. Bills } 370e1f26343SJason M. Bills }; 371e1f26343SJason M. Bills 372e1f26343SJason M. Bills class BMCLogEntry : public Node 373e1f26343SJason M. Bills { 374e1f26343SJason M. Bills public: 375e1f26343SJason M. Bills BMCLogEntry(CrowApp &app) : 376e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/<str>/", 377e1f26343SJason M. Bills std::string()) 378e1f26343SJason M. Bills { 379e1f26343SJason M. Bills entityPrivileges = { 380e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 381e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 382e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 383e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 384e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 385e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 386e1f26343SJason M. Bills } 387e1f26343SJason M. Bills 388e1f26343SJason M. Bills private: 389e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 390e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 391e1f26343SJason M. Bills { 392e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 393e1f26343SJason M. Bills if (params.size() != 1) 394e1f26343SJason M. Bills { 395f12894f8SJason M. Bills messages::internalError(asyncResp->res); 396e1f26343SJason M. Bills return; 397e1f26343SJason M. Bills } 398e1f26343SJason M. Bills // Convert the unique ID back to a timestamp to find the entry 399e1f26343SJason M. Bills boost::string_view tsStr(params[0]); 400e1f26343SJason M. Bills boost::string_view indexStr(params[0]); 401e1f26343SJason M. Bills uint64_t ts = 0; 402e1f26343SJason M. Bills uint16_t index = 0; 403e1f26343SJason M. Bills auto underscorePos = tsStr.find("_"); 404e1f26343SJason M. Bills if (underscorePos == tsStr.npos) 405e1f26343SJason M. Bills { 406e1f26343SJason M. Bills // Timestamp has no index 407e1f26343SJason M. Bills ts = strtoull(tsStr.data(), nullptr, 10); 408e1f26343SJason M. Bills } 409e1f26343SJason M. Bills else 410e1f26343SJason M. Bills { 411e1f26343SJason M. Bills // Timestamp has an index 412e1f26343SJason M. Bills tsStr.remove_suffix(tsStr.size() - underscorePos + 1); 413e1f26343SJason M. Bills ts = strtoull(tsStr.data(), nullptr, 10); 414e1f26343SJason M. Bills indexStr.remove_prefix(underscorePos + 1); 415e1f26343SJason M. Bills index = 416e1f26343SJason M. Bills static_cast<uint16_t>(strtoul(indexStr.data(), nullptr, 10)); 417e1f26343SJason M. Bills } 418e1f26343SJason M. Bills 419e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 420e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 421e1f26343SJason M. Bills if (ret < 0) 422e1f26343SJason M. Bills { 423e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 424f12894f8SJason M. Bills messages::internalError(asyncResp->res); 425e1f26343SJason M. Bills return; 426e1f26343SJason M. Bills } 427e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 428e1f26343SJason M. Bills journalTmp, sd_journal_close); 429e1f26343SJason M. Bills journalTmp = nullptr; 430e1f26343SJason M. Bills // Go to the timestamp in the log and move to the entry at the index 431e1f26343SJason M. Bills ret = sd_journal_seek_realtime_usec(journal.get(), ts); 432e1f26343SJason M. Bills for (int i = 0; i <= index; i++) 433e1f26343SJason M. Bills { 434e1f26343SJason M. Bills sd_journal_next(journal.get()); 435e1f26343SJason M. Bills } 436e1f26343SJason M. Bills if (fillBMCLogEntryJson(params[0], journal.get(), 437e1f26343SJason M. Bills asyncResp->res.jsonValue) != 0) 438e1f26343SJason M. Bills { 439f12894f8SJason M. Bills messages::internalError(asyncResp->res); 440e1f26343SJason M. Bills return; 441e1f26343SJason M. Bills } 442e1f26343SJason M. Bills } 443e1f26343SJason M. Bills }; 444e1f26343SJason M. Bills 445e1f26343SJason M. Bills class CPULogService : public Node 446e1f26343SJason M. Bills { 447e1f26343SJason M. Bills public: 448e1f26343SJason M. Bills template <typename CrowApp> 449e1f26343SJason M. Bills CPULogService(CrowApp &app) : 450e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/") 4511da66f75SEd Tanous { 4521da66f75SEd Tanous // Set the id for SubRoute 4534ed77cd5SEd Tanous Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/CpuLog"; 4541da66f75SEd Tanous entityPrivileges = { 455e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 456e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 457e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 458e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 459e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 460e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 4611da66f75SEd Tanous } 4621da66f75SEd Tanous 4631da66f75SEd Tanous private: 4641da66f75SEd Tanous /** 4651da66f75SEd Tanous * Functions triggers appropriate requests on DBus 4661da66f75SEd Tanous */ 4671da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 4681da66f75SEd Tanous const std::vector<std::string> ¶ms) override 4691da66f75SEd Tanous { 470e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 4711da66f75SEd Tanous // Copy over the static data to include the entries added by SubRoute 472e1f26343SJason M. Bills asyncResp->res.jsonValue = Node::json; 473e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 474e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 475e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 476e1f26343SJason M. Bills "/redfish/v1/" 4771da66f75SEd Tanous "$metadata#LogService.LogService"; 478e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service"; 479e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = "CPU Log Service"; 480e1f26343SJason M. Bills asyncResp->res.jsonValue["Id"] = "CPU Log"; 481e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 482e1f26343SJason M. Bills asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 483e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"] = { 4841da66f75SEd Tanous {"Oem", 4851da66f75SEd Tanous {{"#CpuLog.Immediate", 4861da66f75SEd Tanous {{"target", 4874ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 4881da66f75SEd Tanous "CpuLog.Immediate"}}}}}}; 4891da66f75SEd Tanous 4901da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI 491e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"]["Oem"].push_back( 4921da66f75SEd Tanous {"#CpuLog.SendRawPeci", 4931da66f75SEd Tanous {{"target", 4944ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 4951da66f75SEd Tanous "CpuLog.SendRawPeci"}}}); 4961da66f75SEd Tanous #endif 4971da66f75SEd Tanous } 4981da66f75SEd Tanous }; 4991da66f75SEd Tanous 500e1f26343SJason M. Bills class CPULogEntryCollection : public Node 5011da66f75SEd Tanous { 5021da66f75SEd Tanous public: 5031da66f75SEd Tanous template <typename CrowApp> 504e1f26343SJason M. Bills CPULogEntryCollection(CrowApp &app) : 505e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/") 5061da66f75SEd Tanous { 5071da66f75SEd Tanous // Collections use static ID for SubRoute to add to its parent, but only 5081da66f75SEd Tanous // load dynamic data so the duplicate static members don't get displayed 5091da66f75SEd Tanous Node::json["@odata.id"] = 5104ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries"; 5111da66f75SEd Tanous entityPrivileges = { 512e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 513e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 514e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 515e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 516e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 517e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 5181da66f75SEd Tanous } 5191da66f75SEd Tanous 5201da66f75SEd Tanous private: 5211da66f75SEd Tanous /** 5221da66f75SEd Tanous * Functions triggers appropriate requests on DBus 5231da66f75SEd Tanous */ 5241da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 5251da66f75SEd Tanous const std::vector<std::string> ¶ms) override 5261da66f75SEd Tanous { 527e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 5281da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 5291da66f75SEd Tanous // it has a duplicate entry for members 530e1f26343SJason M. Bills auto getLogEntriesCallback = [asyncResp]( 531e1f26343SJason M. Bills const boost::system::error_code ec, 5321da66f75SEd Tanous const std::vector<std::string> &resp) { 5331da66f75SEd Tanous if (ec) 5341da66f75SEd Tanous { 5351da66f75SEd Tanous if (ec.value() != 5361da66f75SEd Tanous boost::system::errc::no_such_file_or_directory) 5371da66f75SEd Tanous { 5381da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to get entries ec: " 5391da66f75SEd Tanous << ec.message(); 540f12894f8SJason M. Bills messages::internalError(asyncResp->res); 5411da66f75SEd Tanous return; 5421da66f75SEd Tanous } 5431da66f75SEd Tanous } 544e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 5451da66f75SEd Tanous "#LogEntryCollection.LogEntryCollection"; 546e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 5471da66f75SEd Tanous "/redfish/v1/" 5481da66f75SEd Tanous "$metadata#LogEntryCollection.LogEntryCollection"; 549e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 550e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries"; 551e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries"; 552e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 553e1f26343SJason M. Bills "Collection of CPU Log Entries"; 554e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 555e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 5561da66f75SEd Tanous for (const std::string &objpath : resp) 5571da66f75SEd Tanous { 5581da66f75SEd Tanous // Don't list the immediate log 5594ed77cd5SEd Tanous if (objpath.compare(cpuLogImmediatePath) == 0) 5601da66f75SEd Tanous { 5611da66f75SEd Tanous continue; 5621da66f75SEd Tanous } 5634ed77cd5SEd Tanous std::size_t lastPos = objpath.rfind("/"); 5644ed77cd5SEd Tanous if (lastPos != std::string::npos) 5651da66f75SEd Tanous { 566e1f26343SJason M. Bills logEntryArray.push_back( 567e1f26343SJason M. Bills {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/" 568e1f26343SJason M. Bills "CpuLog/Entries/" + 5694ed77cd5SEd Tanous objpath.substr(lastPos + 1)}}); 5701da66f75SEd Tanous } 5711da66f75SEd Tanous } 572e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 573e1f26343SJason M. Bills logEntryArray.size(); 5741da66f75SEd Tanous }; 5751da66f75SEd Tanous crow::connections::systemBus->async_method_call( 5761da66f75SEd Tanous std::move(getLogEntriesCallback), 5771da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", 5781da66f75SEd Tanous "/xyz/openbmc_project/object_mapper", 5791da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0, 5804ed77cd5SEd Tanous std::array<const char *, 1>{cpuLogInterface}); 5811da66f75SEd Tanous } 5821da66f75SEd Tanous }; 5831da66f75SEd Tanous 5841da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog) 5851da66f75SEd Tanous { 5861da66f75SEd Tanous nlohmann::json::const_iterator metaIt = cpuLog.find("metadata"); 5871da66f75SEd Tanous if (metaIt != cpuLog.end()) 5881da66f75SEd Tanous { 5891da66f75SEd Tanous nlohmann::json::const_iterator tsIt = metaIt->find("timestamp"); 5901da66f75SEd Tanous if (tsIt != metaIt->end()) 5911da66f75SEd Tanous { 5921da66f75SEd Tanous const std::string *logTime = tsIt->get_ptr<const std::string *>(); 5931da66f75SEd Tanous if (logTime != nullptr) 5941da66f75SEd Tanous { 5951da66f75SEd Tanous return *logTime; 5961da66f75SEd Tanous } 5971da66f75SEd Tanous } 5981da66f75SEd Tanous } 5991da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to find log timestamp"; 6001da66f75SEd Tanous 6011da66f75SEd Tanous return std::string(); 6021da66f75SEd Tanous } 6031da66f75SEd Tanous 604e1f26343SJason M. Bills class CPULogEntry : public Node 6051da66f75SEd Tanous { 6061da66f75SEd Tanous public: 607e1f26343SJason M. Bills CPULogEntry(CrowApp &app) : 6084ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/", 6091da66f75SEd Tanous std::string()) 6101da66f75SEd Tanous { 6111da66f75SEd Tanous entityPrivileges = { 612e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 613e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 614e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 615e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 616e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 617e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 6181da66f75SEd Tanous } 6191da66f75SEd Tanous 6201da66f75SEd Tanous private: 6211da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 6221da66f75SEd Tanous const std::vector<std::string> ¶ms) override 6231da66f75SEd Tanous { 624e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 6251da66f75SEd Tanous if (params.size() != 1) 6261da66f75SEd Tanous { 627f12894f8SJason M. Bills messages::internalError(asyncResp->res); 6281da66f75SEd Tanous return; 6291da66f75SEd Tanous } 6304ed77cd5SEd Tanous const uint8_t logId = std::atoi(params[0].c_str()); 631e1f26343SJason M. Bills auto getStoredLogCallback = 632e1f26343SJason M. Bills [asyncResp, 6334ed77cd5SEd Tanous logId](const boost::system::error_code ec, 634e1f26343SJason M. Bills const sdbusplus::message::variant<std::string> &resp) { 6351da66f75SEd Tanous if (ec) 6361da66f75SEd Tanous { 637e1f26343SJason M. Bills BMCWEB_LOG_DEBUG << "failed to get log ec: " 638e1f26343SJason M. Bills << ec.message(); 639f12894f8SJason M. Bills messages::internalError(asyncResp->res); 6401da66f75SEd Tanous return; 6411da66f75SEd Tanous } 642e1f26343SJason M. Bills const std::string *log = 643e1f26343SJason M. Bills mapbox::getPtr<const std::string>(resp); 6441da66f75SEd Tanous if (log == nullptr) 6451da66f75SEd Tanous { 646f12894f8SJason M. Bills messages::internalError(asyncResp->res); 6471da66f75SEd Tanous return; 6481da66f75SEd Tanous } 6491da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 6501da66f75SEd Tanous if (j.is_discarded()) 6511da66f75SEd Tanous { 652f12894f8SJason M. Bills messages::internalError(asyncResp->res); 6531da66f75SEd Tanous return; 6541da66f75SEd Tanous } 6551da66f75SEd Tanous std::string t = getLogCreatedTime(j); 656e1f26343SJason M. Bills asyncResp->res.jsonValue = { 6571da66f75SEd Tanous {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 658e1f26343SJason M. Bills {"@odata.context", 659e1f26343SJason M. Bills "/redfish/v1/$metadata#LogEntry.LogEntry"}, 6601da66f75SEd Tanous {"@odata.id", 6614ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" + 6624ed77cd5SEd Tanous std::to_string(logId)}, 6631da66f75SEd Tanous {"Name", "CPU Debug Log"}, 6644ed77cd5SEd Tanous {"Id", logId}, 6651da66f75SEd Tanous {"EntryType", "Oem"}, 6661da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 6671da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 6681da66f75SEd Tanous {"Created", std::move(t)}}; 6691da66f75SEd Tanous }; 6701da66f75SEd Tanous crow::connections::systemBus->async_method_call( 6714ed77cd5SEd Tanous std::move(getStoredLogCallback), cpuLogObject, 6724ed77cd5SEd Tanous cpuLogPath + std::string("/") + std::to_string(logId), 6734ed77cd5SEd Tanous "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log"); 6741da66f75SEd Tanous } 6751da66f75SEd Tanous }; 6761da66f75SEd Tanous 677e1f26343SJason M. Bills class ImmediateCPULog : public Node 6781da66f75SEd Tanous { 6791da66f75SEd Tanous public: 680e1f26343SJason M. Bills ImmediateCPULog(CrowApp &app) : 6814ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 682e1f26343SJason M. Bills "CpuLog.Immediate/") 6831da66f75SEd Tanous { 6841da66f75SEd Tanous entityPrivileges = { 685e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 686e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 687e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 688e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 689e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 690e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 6911da66f75SEd Tanous } 6921da66f75SEd Tanous 6931da66f75SEd Tanous private: 6941da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 6951da66f75SEd Tanous const std::vector<std::string> ¶ms) override 6961da66f75SEd Tanous { 697e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 6981da66f75SEd Tanous static std::unique_ptr<sdbusplus::bus::match::match> 6991da66f75SEd Tanous immediateLogMatcher; 7001da66f75SEd Tanous 7011da66f75SEd Tanous // Only allow one Immediate Log request at a time 7021da66f75SEd Tanous if (immediateLogMatcher != nullptr) 7031da66f75SEd Tanous { 704e1f26343SJason M. Bills asyncResp->res.addHeader("Retry-After", "30"); 705f12894f8SJason M. Bills messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); 7061da66f75SEd Tanous return; 7071da66f75SEd Tanous } 7081da66f75SEd Tanous // Make this static so it survives outside this method 7091da66f75SEd Tanous static boost::asio::deadline_timer timeout(*req.ioService); 7101da66f75SEd Tanous 7111da66f75SEd Tanous timeout.expires_from_now(boost::posix_time::seconds(30)); 712e1f26343SJason M. Bills timeout.async_wait([asyncResp](const boost::system::error_code &ec) { 7131da66f75SEd Tanous immediateLogMatcher = nullptr; 7141da66f75SEd Tanous if (ec) 7151da66f75SEd Tanous { 7161da66f75SEd Tanous // operation_aborted is expected if timer is canceled before 7171da66f75SEd Tanous // completion. 7181da66f75SEd Tanous if (ec != boost::asio::error::operation_aborted) 7191da66f75SEd Tanous { 7201da66f75SEd Tanous BMCWEB_LOG_ERROR << "Async_wait failed " << ec; 7211da66f75SEd Tanous } 7221da66f75SEd Tanous return; 7231da66f75SEd Tanous } 7241da66f75SEd Tanous BMCWEB_LOG_ERROR << "Timed out waiting for immediate log"; 7251da66f75SEd Tanous 726f12894f8SJason M. Bills messages::internalError(asyncResp->res); 7271da66f75SEd Tanous }); 7281da66f75SEd Tanous 729e1f26343SJason M. Bills auto immediateLogMatcherCallback = [asyncResp]( 7301da66f75SEd Tanous sdbusplus::message::message &m) { 7311da66f75SEd Tanous BMCWEB_LOG_DEBUG << "Immediate log available match fired"; 7321da66f75SEd Tanous boost::system::error_code ec; 7331da66f75SEd Tanous timeout.cancel(ec); 7341da66f75SEd Tanous if (ec) 7351da66f75SEd Tanous { 7361da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " << ec; 7371da66f75SEd Tanous } 7384ed77cd5SEd Tanous sdbusplus::message::object_path objPath; 7391da66f75SEd Tanous boost::container::flat_map< 7401da66f75SEd Tanous std::string, 7411da66f75SEd Tanous boost::container::flat_map< 7421da66f75SEd Tanous std::string, sdbusplus::message::variant<std::string>>> 7434ed77cd5SEd Tanous interfacesAdded; 7444ed77cd5SEd Tanous m.read(objPath, interfacesAdded); 7451da66f75SEd Tanous const std::string *log = mapbox::getPtr<const std::string>( 7464ed77cd5SEd Tanous interfacesAdded[cpuLogInterface]["Log"]); 7471da66f75SEd Tanous if (log == nullptr) 7481da66f75SEd Tanous { 749f12894f8SJason M. Bills messages::internalError(asyncResp->res); 7501da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 7511da66f75SEd Tanous // match object inside which this lambda is executing. Once it 7521da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 7531da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 7541da66f75SEd Tanous // be the last thing done. 7551da66f75SEd Tanous immediateLogMatcher = nullptr; 7561da66f75SEd Tanous return; 7571da66f75SEd Tanous } 7581da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 7591da66f75SEd Tanous if (j.is_discarded()) 7601da66f75SEd Tanous { 761f12894f8SJason M. Bills messages::internalError(asyncResp->res); 7621da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 7631da66f75SEd Tanous // match object inside which this lambda is executing. Once it 7641da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 7651da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 7661da66f75SEd Tanous // be the last thing done. 7671da66f75SEd Tanous immediateLogMatcher = nullptr; 7681da66f75SEd Tanous return; 7691da66f75SEd Tanous } 7701da66f75SEd Tanous std::string t = getLogCreatedTime(j); 771e1f26343SJason M. Bills asyncResp->res.jsonValue = { 7721da66f75SEd Tanous {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 7731da66f75SEd Tanous {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 7741da66f75SEd Tanous {"Name", "CPU Debug Log"}, 7751da66f75SEd Tanous {"EntryType", "Oem"}, 7761da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 7771da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 7781da66f75SEd Tanous {"Created", std::move(t)}}; 7791da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 7801da66f75SEd Tanous // match object inside which this lambda is executing. Once it is 7811da66f75SEd Tanous // set to nullptr, the match object will be destroyed and the lambda 7821da66f75SEd Tanous // will lose its context, including res, so it needs to be the last 7831da66f75SEd Tanous // thing done. 7841da66f75SEd Tanous immediateLogMatcher = nullptr; 7851da66f75SEd Tanous }; 7861da66f75SEd Tanous immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>( 7871da66f75SEd Tanous *crow::connections::systemBus, 7881da66f75SEd Tanous sdbusplus::bus::match::rules::interfacesAdded() + 7894ed77cd5SEd Tanous sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath), 7901da66f75SEd Tanous std::move(immediateLogMatcherCallback)); 7911da66f75SEd Tanous 7921da66f75SEd Tanous auto generateImmediateLogCallback = 793e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 7941da66f75SEd Tanous const std::string &resp) { 7951da66f75SEd Tanous if (ec) 7961da66f75SEd Tanous { 7971da66f75SEd Tanous if (ec.value() == 7981da66f75SEd Tanous boost::system::errc::operation_not_supported) 7991da66f75SEd Tanous { 800f12894f8SJason M. Bills messages::resourceInStandby(asyncResp->res); 8011da66f75SEd Tanous } 8021da66f75SEd Tanous else 8031da66f75SEd Tanous { 804f12894f8SJason M. Bills messages::internalError(asyncResp->res); 8051da66f75SEd Tanous } 8061da66f75SEd Tanous boost::system::error_code timeoutec; 8071da66f75SEd Tanous timeout.cancel(timeoutec); 8081da66f75SEd Tanous if (timeoutec) 8091da66f75SEd Tanous { 8101da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " 8111da66f75SEd Tanous << timeoutec; 8121da66f75SEd Tanous } 8131da66f75SEd Tanous immediateLogMatcher = nullptr; 8141da66f75SEd Tanous return; 8151da66f75SEd Tanous } 8161da66f75SEd Tanous }; 8171da66f75SEd Tanous crow::connections::systemBus->async_method_call( 8184ed77cd5SEd Tanous std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath, 8194ed77cd5SEd Tanous cpuLogImmediateInterface, "GenerateImmediateLog"); 8201da66f75SEd Tanous } 8211da66f75SEd Tanous }; 8221da66f75SEd Tanous 823e1f26343SJason M. Bills class SendRawPECI : public Node 8241da66f75SEd Tanous { 8251da66f75SEd Tanous public: 826e1f26343SJason M. Bills SendRawPECI(CrowApp &app) : 8274ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 828e1f26343SJason M. Bills "CpuLog.SendRawPeci/") 8291da66f75SEd Tanous { 8301da66f75SEd Tanous entityPrivileges = { 8311da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 8321da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 8331da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 8341da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 8351da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 8361da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 8371da66f75SEd Tanous } 8381da66f75SEd Tanous 8391da66f75SEd Tanous private: 8401da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 8411da66f75SEd Tanous const std::vector<std::string> ¶ms) override 8421da66f75SEd Tanous { 843e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 8441da66f75SEd Tanous // Get the Raw PECI command from the request 845e1f26343SJason M. Bills nlohmann::json rawPECICmd; 846e1f26343SJason M. Bills if (!json_util::processJsonFromRequest(res, req, rawPECICmd)) 8471da66f75SEd Tanous { 8481da66f75SEd Tanous return; 8491da66f75SEd Tanous } 8501da66f75SEd Tanous // Get the Client Address from the request 851e1f26343SJason M. Bills nlohmann::json::const_iterator caIt = rawPECICmd.find("ClientAddress"); 852e1f26343SJason M. Bills if (caIt == rawPECICmd.end()) 8531da66f75SEd Tanous { 854f12894f8SJason M. Bills messages::propertyMissing(asyncResp->res, "ClientAddress", 855f12894f8SJason M. Bills "/ClientAddress"); 8561da66f75SEd Tanous return; 8571da66f75SEd Tanous } 8581da66f75SEd Tanous const uint64_t *ca = caIt->get_ptr<const uint64_t *>(); 8591da66f75SEd Tanous if (ca == nullptr) 8601da66f75SEd Tanous { 861f12894f8SJason M. Bills messages::propertyValueTypeError(asyncResp->res, caIt->dump(), 862f12894f8SJason M. Bills "ClientAddress", "/ClientAddress"); 8631da66f75SEd Tanous return; 8641da66f75SEd Tanous } 8651da66f75SEd Tanous // Get the Read Length from the request 8661da66f75SEd Tanous const uint8_t clientAddress = static_cast<uint8_t>(*ca); 867e1f26343SJason M. Bills nlohmann::json::const_iterator rlIt = rawPECICmd.find("ReadLength"); 868e1f26343SJason M. Bills if (rlIt == rawPECICmd.end()) 8691da66f75SEd Tanous { 870f12894f8SJason M. Bills messages::propertyMissing(asyncResp->res, "ReadLength", 8711da66f75SEd Tanous "/ReadLength"); 8721da66f75SEd Tanous return; 8731da66f75SEd Tanous } 8741da66f75SEd Tanous const uint64_t *rl = rlIt->get_ptr<const uint64_t *>(); 8751da66f75SEd Tanous if (rl == nullptr) 8761da66f75SEd Tanous { 877f12894f8SJason M. Bills messages::propertyValueTypeError(asyncResp->res, rlIt->dump(), 878f12894f8SJason M. Bills "ReadLength", "/ReadLength"); 8791da66f75SEd Tanous return; 8801da66f75SEd Tanous } 8811da66f75SEd Tanous // Get the PECI Command from the request 8821da66f75SEd Tanous const uint32_t readLength = static_cast<uint32_t>(*rl); 883e1f26343SJason M. Bills nlohmann::json::const_iterator pcIt = rawPECICmd.find("PECICommand"); 884e1f26343SJason M. Bills if (pcIt == rawPECICmd.end()) 8851da66f75SEd Tanous { 886f12894f8SJason M. Bills messages::propertyMissing(asyncResp->res, "PECICommand", 8871da66f75SEd Tanous "/PECICommand"); 8881da66f75SEd Tanous return; 8891da66f75SEd Tanous } 8901da66f75SEd Tanous std::vector<uint8_t> peciCommand; 8911da66f75SEd Tanous for (auto pc : *pcIt) 8921da66f75SEd Tanous { 8931da66f75SEd Tanous const uint64_t *val = pc.get_ptr<const uint64_t *>(); 8941da66f75SEd Tanous if (val == nullptr) 8951da66f75SEd Tanous { 8961da66f75SEd Tanous messages::propertyValueTypeError( 897f12894f8SJason M. Bills asyncResp->res, pc.dump(), 898f12894f8SJason M. Bills "PECICommand/" + std::to_string(peciCommand.size()), 8991da66f75SEd Tanous "/PECICommand"); 9001da66f75SEd Tanous return; 9011da66f75SEd Tanous } 9021da66f75SEd Tanous peciCommand.push_back(static_cast<uint8_t>(*val)); 9031da66f75SEd Tanous } 9041da66f75SEd Tanous // Callback to return the Raw PECI response 905e1f26343SJason M. Bills auto sendRawPECICallback = 906e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 9071da66f75SEd Tanous const std::vector<uint8_t> &resp) { 9081da66f75SEd Tanous if (ec) 9091da66f75SEd Tanous { 9101da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to send PECI command ec: " 9111da66f75SEd Tanous << ec.message(); 912f12894f8SJason M. Bills messages::internalError(asyncResp->res); 9131da66f75SEd Tanous return; 9141da66f75SEd Tanous } 915e1f26343SJason M. Bills asyncResp->res.jsonValue = {{"Name", "PECI Command Response"}, 9161da66f75SEd Tanous {"PECIResponse", resp}}; 9171da66f75SEd Tanous }; 9181da66f75SEd Tanous // Call the SendRawPECI command with the provided data 9191da66f75SEd Tanous crow::connections::systemBus->async_method_call( 920e1f26343SJason M. Bills std::move(sendRawPECICallback), cpuLogObject, cpuLogPath, 921e1f26343SJason M. Bills cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength, 9224ed77cd5SEd Tanous peciCommand); 9231da66f75SEd Tanous } 9241da66f75SEd Tanous }; 9251da66f75SEd Tanous 9261da66f75SEd Tanous } // namespace redfish 927