1*1da66f75SEd Tanous /* 2*1da66f75SEd Tanous // Copyright (c) 2018 Intel Corporation 3*1da66f75SEd Tanous // 4*1da66f75SEd Tanous // Licensed under the Apache License, Version 2.0 (the "License"); 5*1da66f75SEd Tanous // you may not use this file except in compliance with the License. 6*1da66f75SEd Tanous // You may obtain a copy of the License at 7*1da66f75SEd Tanous // 8*1da66f75SEd Tanous // http://www.apache.org/licenses/LICENSE-2.0 9*1da66f75SEd Tanous // 10*1da66f75SEd Tanous // Unless required by applicable law or agreed to in writing, software 11*1da66f75SEd Tanous // distributed under the License is distributed on an "AS IS" BASIS, 12*1da66f75SEd Tanous // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*1da66f75SEd Tanous // See the License for the specific language governing permissions and 14*1da66f75SEd Tanous // limitations under the License. 15*1da66f75SEd Tanous */ 16*1da66f75SEd Tanous #pragma once 17*1da66f75SEd Tanous 18*1da66f75SEd Tanous #include "node.hpp" 19*1da66f75SEd Tanous 20*1da66f75SEd Tanous #include <boost/container/flat_map.hpp> 21*1da66f75SEd Tanous #include <experimental/filesystem> 22*1da66f75SEd Tanous 23*1da66f75SEd Tanous namespace redfish 24*1da66f75SEd Tanous { 25*1da66f75SEd Tanous 26*1da66f75SEd Tanous constexpr char const *CPU_LOG_OBJECT = "com.intel.CpuDebugLog"; 27*1da66f75SEd Tanous constexpr char const *CPU_LOG_PATH = "/com/intel/CpuDebugLog"; 28*1da66f75SEd Tanous constexpr char const *CPU_LOG_IMMEDIATE_PATH = 29*1da66f75SEd Tanous "/com/intel/CpuDebugLog/Immediate"; 30*1da66f75SEd Tanous constexpr char const *CPU_LOG_INTERFACE = "com.intel.CpuDebugLog"; 31*1da66f75SEd Tanous constexpr char const *CPU_LOG_IMMEDIATE_INTERFACE = 32*1da66f75SEd Tanous "com.intel.CpuDebugLog.Immediate"; 33*1da66f75SEd Tanous constexpr char const *CPU_LOG_RAW_PECI_INTERFACE = 34*1da66f75SEd Tanous "com.intel.CpuDebugLog.SendRawPeci"; 35*1da66f75SEd Tanous 36*1da66f75SEd Tanous namespace fs = std::experimental::filesystem; 37*1da66f75SEd Tanous 38*1da66f75SEd Tanous class LogServiceCollection : public Node 39*1da66f75SEd Tanous { 40*1da66f75SEd Tanous public: 41*1da66f75SEd Tanous template <typename CrowApp> 42*1da66f75SEd Tanous LogServiceCollection(CrowApp &app) : 43*1da66f75SEd Tanous Node(app, "/redfish/v1/Managers/openbmc/LogServices/") 44*1da66f75SEd Tanous { 45*1da66f75SEd Tanous // Collections use static ID for SubRoute to add to its parent, but only 46*1da66f75SEd Tanous // load dynamic data so the duplicate static members don't get displayed 47*1da66f75SEd Tanous Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/LogServices"; 48*1da66f75SEd Tanous entityPrivileges = { 49*1da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 50*1da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 51*1da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 52*1da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 53*1da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 54*1da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 55*1da66f75SEd Tanous } 56*1da66f75SEd Tanous 57*1da66f75SEd Tanous private: 58*1da66f75SEd Tanous /** 59*1da66f75SEd Tanous * Functions triggers appropriate requests on DBus 60*1da66f75SEd Tanous */ 61*1da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 62*1da66f75SEd Tanous const std::vector<std::string> ¶ms) override 63*1da66f75SEd Tanous { 64*1da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 65*1da66f75SEd Tanous // it has a duplicate entry for members 66*1da66f75SEd Tanous res.jsonValue["@odata.type"] = 67*1da66f75SEd Tanous "#LogServiceCollection.LogServiceCollection"; 68*1da66f75SEd Tanous res.jsonValue["@odata.context"] = 69*1da66f75SEd Tanous "/redfish/v1/" 70*1da66f75SEd Tanous "$metadata#LogServiceCollection.LogServiceCollection"; 71*1da66f75SEd Tanous res.jsonValue["@odata.id"] = "/redfish/v1/Managers/openbmc/LogServices"; 72*1da66f75SEd Tanous res.jsonValue["Name"] = "Open BMC Log Services Collection"; 73*1da66f75SEd Tanous res.jsonValue["Description"] = 74*1da66f75SEd Tanous "Collection of LogServices for this Manager"; 75*1da66f75SEd Tanous nlohmann::json &logserviceArray = res.jsonValue["Members"]; 76*1da66f75SEd Tanous logserviceArray = nlohmann::json::array(); 77*1da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG 78*1da66f75SEd Tanous logserviceArray.push_back( 79*1da66f75SEd Tanous {{"@odata.id", "/redfish/v1/Managers/openbmc/LogServices/CpuLog"}}); 80*1da66f75SEd Tanous #endif 81*1da66f75SEd Tanous res.jsonValue["Members@odata.count"] = logserviceArray.size(); 82*1da66f75SEd Tanous res.end(); 83*1da66f75SEd Tanous } 84*1da66f75SEd Tanous }; 85*1da66f75SEd Tanous 86*1da66f75SEd Tanous class CpuLogService : public Node 87*1da66f75SEd Tanous { 88*1da66f75SEd Tanous public: 89*1da66f75SEd Tanous template <typename CrowApp> 90*1da66f75SEd Tanous CpuLogService(CrowApp &app) : 91*1da66f75SEd Tanous Node(app, "/redfish/v1/Managers/openbmc/LogServices/CpuLog") 92*1da66f75SEd Tanous { 93*1da66f75SEd Tanous // Set the id for SubRoute 94*1da66f75SEd Tanous Node::json["@odata.id"] = 95*1da66f75SEd Tanous "/redfish/v1/Managers/openbmc/LogServices/CpuLog"; 96*1da66f75SEd Tanous entityPrivileges = { 97*1da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 98*1da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 99*1da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 100*1da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 101*1da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 102*1da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 103*1da66f75SEd Tanous } 104*1da66f75SEd Tanous 105*1da66f75SEd Tanous private: 106*1da66f75SEd Tanous /** 107*1da66f75SEd Tanous * Functions triggers appropriate requests on DBus 108*1da66f75SEd Tanous */ 109*1da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 110*1da66f75SEd Tanous const std::vector<std::string> ¶ms) override 111*1da66f75SEd Tanous { 112*1da66f75SEd Tanous // Copy over the static data to include the entries added by SubRoute 113*1da66f75SEd Tanous res.jsonValue = Node::json; 114*1da66f75SEd Tanous res.jsonValue["@odata.type"] = "#LogService.v1_1_0.LogService"; 115*1da66f75SEd Tanous res.jsonValue["@odata.context"] = "/redfish/v1/" 116*1da66f75SEd Tanous "$metadata#LogService.LogService"; 117*1da66f75SEd Tanous res.jsonValue["Name"] = "Open BMC CPU Log Service"; 118*1da66f75SEd Tanous res.jsonValue["Description"] = "CPU Log Service"; 119*1da66f75SEd Tanous res.jsonValue["Id"] = "CPU Log"; 120*1da66f75SEd Tanous res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 121*1da66f75SEd Tanous res.jsonValue["MaxNumberOfRecords"] = 3; 122*1da66f75SEd Tanous res.jsonValue["Actions"] = { 123*1da66f75SEd Tanous {"Oem", 124*1da66f75SEd Tanous {{"#CpuLog.Immediate", 125*1da66f75SEd Tanous {{"target", 126*1da66f75SEd Tanous "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Actions/Oem/" 127*1da66f75SEd Tanous "CpuLog.Immediate"}}}}}}; 128*1da66f75SEd Tanous 129*1da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI 130*1da66f75SEd Tanous res.jsonValue["Actions"]["Oem"].push_back( 131*1da66f75SEd Tanous {"#CpuLog.SendRawPeci", 132*1da66f75SEd Tanous {{"target", 133*1da66f75SEd Tanous "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Actions/Oem/" 134*1da66f75SEd Tanous "CpuLog.SendRawPeci"}}}); 135*1da66f75SEd Tanous #endif 136*1da66f75SEd Tanous res.end(); 137*1da66f75SEd Tanous } 138*1da66f75SEd Tanous }; 139*1da66f75SEd Tanous 140*1da66f75SEd Tanous class CpuLogEntryCollection : public Node 141*1da66f75SEd Tanous { 142*1da66f75SEd Tanous public: 143*1da66f75SEd Tanous template <typename CrowApp> 144*1da66f75SEd Tanous CpuLogEntryCollection(CrowApp &app) : 145*1da66f75SEd Tanous Node(app, "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Entries") 146*1da66f75SEd Tanous { 147*1da66f75SEd Tanous // Collections use static ID for SubRoute to add to its parent, but only 148*1da66f75SEd Tanous // load dynamic data so the duplicate static members don't get displayed 149*1da66f75SEd Tanous Node::json["@odata.id"] = 150*1da66f75SEd Tanous "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Entries"; 151*1da66f75SEd Tanous entityPrivileges = { 152*1da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 153*1da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 154*1da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 155*1da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 156*1da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 157*1da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 158*1da66f75SEd Tanous } 159*1da66f75SEd Tanous 160*1da66f75SEd Tanous private: 161*1da66f75SEd Tanous /** 162*1da66f75SEd Tanous * Functions triggers appropriate requests on DBus 163*1da66f75SEd Tanous */ 164*1da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 165*1da66f75SEd Tanous const std::vector<std::string> ¶ms) override 166*1da66f75SEd Tanous { 167*1da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 168*1da66f75SEd Tanous // it has a duplicate entry for members 169*1da66f75SEd Tanous auto getLogEntriesCallback = 170*1da66f75SEd Tanous [&res](const boost::system::error_code ec, 171*1da66f75SEd Tanous const std::vector<std::string> &resp) { 172*1da66f75SEd Tanous if (ec) 173*1da66f75SEd Tanous { 174*1da66f75SEd Tanous if (ec.value() != 175*1da66f75SEd Tanous boost::system::errc::no_such_file_or_directory) 176*1da66f75SEd Tanous { 177*1da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to get entries ec: " 178*1da66f75SEd Tanous << ec.message(); 179*1da66f75SEd Tanous res.result( 180*1da66f75SEd Tanous boost::beast::http::status::internal_server_error); 181*1da66f75SEd Tanous res.end(); 182*1da66f75SEd Tanous return; 183*1da66f75SEd Tanous } 184*1da66f75SEd Tanous } 185*1da66f75SEd Tanous res.jsonValue["@odata.type"] = 186*1da66f75SEd Tanous "#LogEntryCollection.LogEntryCollection"; 187*1da66f75SEd Tanous res.jsonValue["@odata.context"] = 188*1da66f75SEd Tanous "/redfish/v1/" 189*1da66f75SEd Tanous "$metadata#LogEntryCollection.LogEntryCollection"; 190*1da66f75SEd Tanous res.jsonValue["Name"] = "Open BMC CPU Log Entries"; 191*1da66f75SEd Tanous res.jsonValue["Description"] = "Collection of CPU Log Entries"; 192*1da66f75SEd Tanous nlohmann::json &logentry_array = res.jsonValue["Members"]; 193*1da66f75SEd Tanous logentry_array = nlohmann::json::array(); 194*1da66f75SEd Tanous for (const std::string &objpath : resp) 195*1da66f75SEd Tanous { 196*1da66f75SEd Tanous // Don't list the immediate log 197*1da66f75SEd Tanous if (objpath.compare(CPU_LOG_IMMEDIATE_PATH) == 0) 198*1da66f75SEd Tanous { 199*1da66f75SEd Tanous continue; 200*1da66f75SEd Tanous } 201*1da66f75SEd Tanous std::size_t last_pos = objpath.rfind("/"); 202*1da66f75SEd Tanous if (last_pos != std::string::npos) 203*1da66f75SEd Tanous { 204*1da66f75SEd Tanous logentry_array.push_back( 205*1da66f75SEd Tanous {{"@odata.id", "/redfish/v1/Managers/openbmc/" 206*1da66f75SEd Tanous "LogServices/CpuLog/Entries/" + 207*1da66f75SEd Tanous objpath.substr(last_pos + 1)}}); 208*1da66f75SEd Tanous } 209*1da66f75SEd Tanous } 210*1da66f75SEd Tanous res.jsonValue["Members@odata.count"] = logentry_array.size(); 211*1da66f75SEd Tanous res.end(); 212*1da66f75SEd Tanous }; 213*1da66f75SEd Tanous crow::connections::systemBus->async_method_call( 214*1da66f75SEd Tanous std::move(getLogEntriesCallback), 215*1da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", 216*1da66f75SEd Tanous "/xyz/openbmc_project/object_mapper", 217*1da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0, 218*1da66f75SEd Tanous std::array<const char *, 1>{CPU_LOG_INTERFACE}); 219*1da66f75SEd Tanous } 220*1da66f75SEd Tanous }; 221*1da66f75SEd Tanous 222*1da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog) 223*1da66f75SEd Tanous { 224*1da66f75SEd Tanous nlohmann::json::const_iterator metaIt = cpuLog.find("metadata"); 225*1da66f75SEd Tanous if (metaIt != cpuLog.end()) 226*1da66f75SEd Tanous { 227*1da66f75SEd Tanous nlohmann::json::const_iterator tsIt = metaIt->find("timestamp"); 228*1da66f75SEd Tanous if (tsIt != metaIt->end()) 229*1da66f75SEd Tanous { 230*1da66f75SEd Tanous const std::string *logTime = tsIt->get_ptr<const std::string *>(); 231*1da66f75SEd Tanous if (logTime != nullptr) 232*1da66f75SEd Tanous { 233*1da66f75SEd Tanous return *logTime; 234*1da66f75SEd Tanous } 235*1da66f75SEd Tanous } 236*1da66f75SEd Tanous } 237*1da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to find log timestamp"; 238*1da66f75SEd Tanous 239*1da66f75SEd Tanous return std::string(); 240*1da66f75SEd Tanous } 241*1da66f75SEd Tanous 242*1da66f75SEd Tanous class CpuLogEntry : public Node 243*1da66f75SEd Tanous { 244*1da66f75SEd Tanous public: 245*1da66f75SEd Tanous CpuLogEntry(CrowApp &app) : 246*1da66f75SEd Tanous Node(app, 247*1da66f75SEd Tanous "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Entries/<str>/", 248*1da66f75SEd Tanous std::string()) 249*1da66f75SEd Tanous { 250*1da66f75SEd Tanous entityPrivileges = { 251*1da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 252*1da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 253*1da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 254*1da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 255*1da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 256*1da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 257*1da66f75SEd Tanous } 258*1da66f75SEd Tanous 259*1da66f75SEd Tanous private: 260*1da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 261*1da66f75SEd Tanous const std::vector<std::string> ¶ms) override 262*1da66f75SEd Tanous { 263*1da66f75SEd Tanous if (params.size() != 1) 264*1da66f75SEd Tanous { 265*1da66f75SEd Tanous res.result(boost::beast::http::status::internal_server_error); 266*1da66f75SEd Tanous res.end(); 267*1da66f75SEd Tanous return; 268*1da66f75SEd Tanous } 269*1da66f75SEd Tanous const uint8_t log_id = std::atoi(params[0].c_str()); 270*1da66f75SEd Tanous auto getStoredLogCallback = [&res, 271*1da66f75SEd Tanous log_id](const boost::system::error_code ec, 272*1da66f75SEd Tanous const sdbusplus::message::variant< 273*1da66f75SEd Tanous std::string> &resp) { 274*1da66f75SEd Tanous if (ec) 275*1da66f75SEd Tanous { 276*1da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message(); 277*1da66f75SEd Tanous res.result(boost::beast::http::status::internal_server_error); 278*1da66f75SEd Tanous res.end(); 279*1da66f75SEd Tanous return; 280*1da66f75SEd Tanous } 281*1da66f75SEd Tanous const std::string *log = mapbox::getPtr<const std::string>(resp); 282*1da66f75SEd Tanous if (log == nullptr) 283*1da66f75SEd Tanous { 284*1da66f75SEd Tanous res.result(boost::beast::http::status::internal_server_error); 285*1da66f75SEd Tanous res.end(); 286*1da66f75SEd Tanous return; 287*1da66f75SEd Tanous } 288*1da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 289*1da66f75SEd Tanous if (j.is_discarded()) 290*1da66f75SEd Tanous { 291*1da66f75SEd Tanous res.result(boost::beast::http::status::internal_server_error); 292*1da66f75SEd Tanous res.end(); 293*1da66f75SEd Tanous return; 294*1da66f75SEd Tanous } 295*1da66f75SEd Tanous std::string t = getLogCreatedTime(j); 296*1da66f75SEd Tanous res.jsonValue = { 297*1da66f75SEd Tanous {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 298*1da66f75SEd Tanous {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 299*1da66f75SEd Tanous {"@odata.id", 300*1da66f75SEd Tanous "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Entries/" + 301*1da66f75SEd Tanous std::to_string(log_id)}, 302*1da66f75SEd Tanous {"Name", "CPU Debug Log"}, 303*1da66f75SEd Tanous {"Id", log_id}, 304*1da66f75SEd Tanous {"EntryType", "Oem"}, 305*1da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 306*1da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 307*1da66f75SEd Tanous {"Created", std::move(t)}}; 308*1da66f75SEd Tanous res.end(); 309*1da66f75SEd Tanous }; 310*1da66f75SEd Tanous crow::connections::systemBus->async_method_call( 311*1da66f75SEd Tanous std::move(getStoredLogCallback), CPU_LOG_OBJECT, 312*1da66f75SEd Tanous CPU_LOG_PATH + std::string("/") + std::to_string(log_id), 313*1da66f75SEd Tanous "org.freedesktop.DBus.Properties", "Get", CPU_LOG_INTERFACE, "Log"); 314*1da66f75SEd Tanous } 315*1da66f75SEd Tanous }; 316*1da66f75SEd Tanous 317*1da66f75SEd Tanous class ImmediateCpuLog : public Node 318*1da66f75SEd Tanous { 319*1da66f75SEd Tanous public: 320*1da66f75SEd Tanous ImmediateCpuLog(CrowApp &app) : 321*1da66f75SEd Tanous Node(app, "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Actions/Oem/" 322*1da66f75SEd Tanous "CpuLog.Immediate") 323*1da66f75SEd Tanous { 324*1da66f75SEd Tanous entityPrivileges = { 325*1da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 326*1da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 327*1da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 328*1da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 329*1da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 330*1da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 331*1da66f75SEd Tanous } 332*1da66f75SEd Tanous 333*1da66f75SEd Tanous private: 334*1da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 335*1da66f75SEd Tanous const std::vector<std::string> ¶ms) override 336*1da66f75SEd Tanous { 337*1da66f75SEd Tanous static std::unique_ptr<sdbusplus::bus::match::match> 338*1da66f75SEd Tanous immediateLogMatcher; 339*1da66f75SEd Tanous 340*1da66f75SEd Tanous // Only allow one Immediate Log request at a time 341*1da66f75SEd Tanous if (immediateLogMatcher != nullptr) 342*1da66f75SEd Tanous { 343*1da66f75SEd Tanous res.addHeader("Retry-After", "30"); 344*1da66f75SEd Tanous res.result(boost::beast::http::status::service_unavailable); 345*1da66f75SEd Tanous messages::addMessageToJson( 346*1da66f75SEd Tanous res.jsonValue, messages::serviceTemporarilyUnavailable("30"), 347*1da66f75SEd Tanous "/CpuLog.Immediate"); 348*1da66f75SEd Tanous res.end(); 349*1da66f75SEd Tanous return; 350*1da66f75SEd Tanous } 351*1da66f75SEd Tanous // Make this static so it survives outside this method 352*1da66f75SEd Tanous static boost::asio::deadline_timer timeout(*req.ioService); 353*1da66f75SEd Tanous 354*1da66f75SEd Tanous timeout.expires_from_now(boost::posix_time::seconds(30)); 355*1da66f75SEd Tanous timeout.async_wait([&res](const boost::system::error_code &ec) { 356*1da66f75SEd Tanous immediateLogMatcher = nullptr; 357*1da66f75SEd Tanous if (ec) 358*1da66f75SEd Tanous { 359*1da66f75SEd Tanous // operation_aborted is expected if timer is canceled before 360*1da66f75SEd Tanous // completion. 361*1da66f75SEd Tanous if (ec != boost::asio::error::operation_aborted) 362*1da66f75SEd Tanous { 363*1da66f75SEd Tanous BMCWEB_LOG_ERROR << "Async_wait failed " << ec; 364*1da66f75SEd Tanous } 365*1da66f75SEd Tanous return; 366*1da66f75SEd Tanous } 367*1da66f75SEd Tanous BMCWEB_LOG_ERROR << "Timed out waiting for immediate log"; 368*1da66f75SEd Tanous 369*1da66f75SEd Tanous res.result(boost::beast::http::status::internal_server_error); 370*1da66f75SEd Tanous res.end(); 371*1da66f75SEd Tanous }); 372*1da66f75SEd Tanous 373*1da66f75SEd Tanous auto immediateLogMatcherCallback = [&res]( 374*1da66f75SEd Tanous sdbusplus::message::message &m) { 375*1da66f75SEd Tanous BMCWEB_LOG_DEBUG << "Immediate log available match fired"; 376*1da66f75SEd Tanous boost::system::error_code ec; 377*1da66f75SEd Tanous timeout.cancel(ec); 378*1da66f75SEd Tanous if (ec) 379*1da66f75SEd Tanous { 380*1da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " << ec; 381*1da66f75SEd Tanous } 382*1da66f75SEd Tanous sdbusplus::message::object_path obj_path; 383*1da66f75SEd Tanous boost::container::flat_map< 384*1da66f75SEd Tanous std::string, 385*1da66f75SEd Tanous boost::container::flat_map< 386*1da66f75SEd Tanous std::string, sdbusplus::message::variant<std::string>>> 387*1da66f75SEd Tanous interfaces_added; 388*1da66f75SEd Tanous m.read(obj_path, interfaces_added); 389*1da66f75SEd Tanous const std::string *log = mapbox::getPtr<const std::string>( 390*1da66f75SEd Tanous interfaces_added[CPU_LOG_INTERFACE]["Log"]); 391*1da66f75SEd Tanous if (log == nullptr) 392*1da66f75SEd Tanous { 393*1da66f75SEd Tanous res.result(boost::beast::http::status::internal_server_error); 394*1da66f75SEd Tanous res.end(); 395*1da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 396*1da66f75SEd Tanous // match object inside which this lambda is executing. Once it 397*1da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 398*1da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 399*1da66f75SEd Tanous // be the last thing done. 400*1da66f75SEd Tanous immediateLogMatcher = nullptr; 401*1da66f75SEd Tanous return; 402*1da66f75SEd Tanous } 403*1da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 404*1da66f75SEd Tanous if (j.is_discarded()) 405*1da66f75SEd Tanous { 406*1da66f75SEd Tanous res.result(boost::beast::http::status::internal_server_error); 407*1da66f75SEd Tanous res.end(); 408*1da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 409*1da66f75SEd Tanous // match object inside which this lambda is executing. Once it 410*1da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 411*1da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 412*1da66f75SEd Tanous // be the last thing done. 413*1da66f75SEd Tanous immediateLogMatcher = nullptr; 414*1da66f75SEd Tanous return; 415*1da66f75SEd Tanous } 416*1da66f75SEd Tanous std::string t = getLogCreatedTime(j); 417*1da66f75SEd Tanous res.jsonValue = { 418*1da66f75SEd Tanous {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 419*1da66f75SEd Tanous {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 420*1da66f75SEd Tanous {"Name", "CPU Debug Log"}, 421*1da66f75SEd Tanous {"EntryType", "Oem"}, 422*1da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 423*1da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 424*1da66f75SEd Tanous {"Created", std::move(t)}}; 425*1da66f75SEd Tanous res.end(); 426*1da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 427*1da66f75SEd Tanous // match object inside which this lambda is executing. Once it is 428*1da66f75SEd Tanous // set to nullptr, the match object will be destroyed and the lambda 429*1da66f75SEd Tanous // will lose its context, including res, so it needs to be the last 430*1da66f75SEd Tanous // thing done. 431*1da66f75SEd Tanous immediateLogMatcher = nullptr; 432*1da66f75SEd Tanous }; 433*1da66f75SEd Tanous immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>( 434*1da66f75SEd Tanous *crow::connections::systemBus, 435*1da66f75SEd Tanous sdbusplus::bus::match::rules::interfacesAdded() + 436*1da66f75SEd Tanous sdbusplus::bus::match::rules::argNpath(0, 437*1da66f75SEd Tanous CPU_LOG_IMMEDIATE_PATH), 438*1da66f75SEd Tanous std::move(immediateLogMatcherCallback)); 439*1da66f75SEd Tanous 440*1da66f75SEd Tanous auto generateImmediateLogCallback = 441*1da66f75SEd Tanous [&res](const boost::system::error_code ec, 442*1da66f75SEd Tanous const std::string &resp) { 443*1da66f75SEd Tanous if (ec) 444*1da66f75SEd Tanous { 445*1da66f75SEd Tanous if (ec.value() == 446*1da66f75SEd Tanous boost::system::errc::operation_not_supported) 447*1da66f75SEd Tanous { 448*1da66f75SEd Tanous messages::addMessageToJson( 449*1da66f75SEd Tanous res.jsonValue, messages::resourceInStandby(), 450*1da66f75SEd Tanous "/CpuLog.Immediate"); 451*1da66f75SEd Tanous res.result( 452*1da66f75SEd Tanous boost::beast::http::status::service_unavailable); 453*1da66f75SEd Tanous } 454*1da66f75SEd Tanous else 455*1da66f75SEd Tanous { 456*1da66f75SEd Tanous res.result( 457*1da66f75SEd Tanous boost::beast::http::status::internal_server_error); 458*1da66f75SEd Tanous } 459*1da66f75SEd Tanous res.end(); 460*1da66f75SEd Tanous boost::system::error_code timeoutec; 461*1da66f75SEd Tanous timeout.cancel(timeoutec); 462*1da66f75SEd Tanous if (timeoutec) 463*1da66f75SEd Tanous { 464*1da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " 465*1da66f75SEd Tanous << timeoutec; 466*1da66f75SEd Tanous } 467*1da66f75SEd Tanous immediateLogMatcher = nullptr; 468*1da66f75SEd Tanous return; 469*1da66f75SEd Tanous } 470*1da66f75SEd Tanous }; 471*1da66f75SEd Tanous crow::connections::systemBus->async_method_call( 472*1da66f75SEd Tanous std::move(generateImmediateLogCallback), CPU_LOG_OBJECT, 473*1da66f75SEd Tanous CPU_LOG_PATH, CPU_LOG_IMMEDIATE_INTERFACE, "GenerateImmediateLog"); 474*1da66f75SEd Tanous } 475*1da66f75SEd Tanous }; 476*1da66f75SEd Tanous 477*1da66f75SEd Tanous class SendRawPeci : public Node 478*1da66f75SEd Tanous { 479*1da66f75SEd Tanous public: 480*1da66f75SEd Tanous SendRawPeci(CrowApp &app) : 481*1da66f75SEd Tanous Node(app, "/redfish/v1/Managers/openbmc/LogServices/CpuLog/Actions/Oem/" 482*1da66f75SEd Tanous "CpuLog.SendRawPeci") 483*1da66f75SEd Tanous { 484*1da66f75SEd Tanous entityPrivileges = { 485*1da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 486*1da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 487*1da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 488*1da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 489*1da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 490*1da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 491*1da66f75SEd Tanous } 492*1da66f75SEd Tanous 493*1da66f75SEd Tanous private: 494*1da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 495*1da66f75SEd Tanous const std::vector<std::string> ¶ms) override 496*1da66f75SEd Tanous { 497*1da66f75SEd Tanous // Get the Raw PECI command from the request 498*1da66f75SEd Tanous nlohmann::json rawPeciCmd; 499*1da66f75SEd Tanous if (!json_util::processJsonFromRequest(res, req, rawPeciCmd)) 500*1da66f75SEd Tanous { 501*1da66f75SEd Tanous return; 502*1da66f75SEd Tanous } 503*1da66f75SEd Tanous // Get the Client Address from the request 504*1da66f75SEd Tanous nlohmann::json::const_iterator caIt = rawPeciCmd.find("ClientAddress"); 505*1da66f75SEd Tanous if (caIt == rawPeciCmd.end()) 506*1da66f75SEd Tanous { 507*1da66f75SEd Tanous messages::addMessageToJson( 508*1da66f75SEd Tanous res.jsonValue, messages::propertyMissing("ClientAddress"), 509*1da66f75SEd Tanous "/ClientAddress"); 510*1da66f75SEd Tanous res.result(boost::beast::http::status::bad_request); 511*1da66f75SEd Tanous res.end(); 512*1da66f75SEd Tanous return; 513*1da66f75SEd Tanous } 514*1da66f75SEd Tanous const uint64_t *ca = caIt->get_ptr<const uint64_t *>(); 515*1da66f75SEd Tanous if (ca == nullptr) 516*1da66f75SEd Tanous { 517*1da66f75SEd Tanous messages::addMessageToJson( 518*1da66f75SEd Tanous res.jsonValue, 519*1da66f75SEd Tanous messages::propertyValueTypeError(caIt->dump(), "ClientAddress"), 520*1da66f75SEd Tanous "/ClientAddress"); 521*1da66f75SEd Tanous res.result(boost::beast::http::status::bad_request); 522*1da66f75SEd Tanous res.end(); 523*1da66f75SEd Tanous return; 524*1da66f75SEd Tanous } 525*1da66f75SEd Tanous // Get the Read Length from the request 526*1da66f75SEd Tanous const uint8_t clientAddress = static_cast<uint8_t>(*ca); 527*1da66f75SEd Tanous nlohmann::json::const_iterator rlIt = rawPeciCmd.find("ReadLength"); 528*1da66f75SEd Tanous if (rlIt == rawPeciCmd.end()) 529*1da66f75SEd Tanous { 530*1da66f75SEd Tanous messages::addMessageToJson(res.jsonValue, 531*1da66f75SEd Tanous messages::propertyMissing("ReadLength"), 532*1da66f75SEd Tanous "/ReadLength"); 533*1da66f75SEd Tanous res.result(boost::beast::http::status::bad_request); 534*1da66f75SEd Tanous res.end(); 535*1da66f75SEd Tanous return; 536*1da66f75SEd Tanous } 537*1da66f75SEd Tanous const uint64_t *rl = rlIt->get_ptr<const uint64_t *>(); 538*1da66f75SEd Tanous if (rl == nullptr) 539*1da66f75SEd Tanous { 540*1da66f75SEd Tanous messages::addMessageToJson( 541*1da66f75SEd Tanous res.jsonValue, 542*1da66f75SEd Tanous messages::propertyValueTypeError(rlIt->dump(), "ReadLength"), 543*1da66f75SEd Tanous "/ReadLength"); 544*1da66f75SEd Tanous res.result(boost::beast::http::status::bad_request); 545*1da66f75SEd Tanous res.end(); 546*1da66f75SEd Tanous return; 547*1da66f75SEd Tanous } 548*1da66f75SEd Tanous // Get the PECI Command from the request 549*1da66f75SEd Tanous const uint32_t readLength = static_cast<uint32_t>(*rl); 550*1da66f75SEd Tanous nlohmann::json::const_iterator pcIt = rawPeciCmd.find("PECICommand"); 551*1da66f75SEd Tanous if (pcIt == rawPeciCmd.end()) 552*1da66f75SEd Tanous { 553*1da66f75SEd Tanous messages::addMessageToJson(res.jsonValue, 554*1da66f75SEd Tanous messages::propertyMissing("PECICommand"), 555*1da66f75SEd Tanous "/PECICommand"); 556*1da66f75SEd Tanous res.result(boost::beast::http::status::bad_request); 557*1da66f75SEd Tanous res.end(); 558*1da66f75SEd Tanous return; 559*1da66f75SEd Tanous } 560*1da66f75SEd Tanous std::vector<uint8_t> peciCommand; 561*1da66f75SEd Tanous for (auto pc : *pcIt) 562*1da66f75SEd Tanous { 563*1da66f75SEd Tanous const uint64_t *val = pc.get_ptr<const uint64_t *>(); 564*1da66f75SEd Tanous if (val == nullptr) 565*1da66f75SEd Tanous { 566*1da66f75SEd Tanous messages::addMessageToJson( 567*1da66f75SEd Tanous res.jsonValue, 568*1da66f75SEd Tanous messages::propertyValueTypeError( 569*1da66f75SEd Tanous pc.dump(), 570*1da66f75SEd Tanous "PECICommand/" + std::to_string(peciCommand.size())), 571*1da66f75SEd Tanous "/PECICommand"); 572*1da66f75SEd Tanous res.result(boost::beast::http::status::bad_request); 573*1da66f75SEd Tanous res.end(); 574*1da66f75SEd Tanous return; 575*1da66f75SEd Tanous } 576*1da66f75SEd Tanous peciCommand.push_back(static_cast<uint8_t>(*val)); 577*1da66f75SEd Tanous } 578*1da66f75SEd Tanous // Callback to return the Raw PECI response 579*1da66f75SEd Tanous auto sendRawPeciCallback = [&res](const boost::system::error_code ec, 580*1da66f75SEd Tanous const std::vector<uint8_t> &resp) { 581*1da66f75SEd Tanous if (ec) 582*1da66f75SEd Tanous { 583*1da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to send PECI command ec: " 584*1da66f75SEd Tanous << ec.message(); 585*1da66f75SEd Tanous res.result(boost::beast::http::status::internal_server_error); 586*1da66f75SEd Tanous res.end(); 587*1da66f75SEd Tanous return; 588*1da66f75SEd Tanous } 589*1da66f75SEd Tanous res.jsonValue = {{"Name", "PECI Command Response"}, 590*1da66f75SEd Tanous {"PECIResponse", resp}}; 591*1da66f75SEd Tanous res.end(); 592*1da66f75SEd Tanous }; 593*1da66f75SEd Tanous // Call the SendRawPECI command with the provided data 594*1da66f75SEd Tanous crow::connections::systemBus->async_method_call( 595*1da66f75SEd Tanous std::move(sendRawPeciCallback), CPU_LOG_OBJECT, CPU_LOG_PATH, 596*1da66f75SEd Tanous CPU_LOG_RAW_PECI_INTERFACE, "SendRawPeci", clientAddress, 597*1da66f75SEd Tanous readLength, peciCommand); 598*1da66f75SEd Tanous } 599*1da66f75SEd Tanous }; 600*1da66f75SEd Tanous 601*1da66f75SEd Tanous } // namespace redfish 602