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 40*16428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal, 41*16428a1aSJason M. Bills const boost::string_view &field, 42*16428a1aSJason M. Bills boost::string_view &contents) 43*16428a1aSJason M. Bills { 44*16428a1aSJason M. Bills const char *data = nullptr; 45*16428a1aSJason M. Bills size_t length = 0; 46*16428a1aSJason M. Bills int ret = 0; 47*16428a1aSJason M. Bills // Get the metadata from the requested field of the journal entry 48*16428a1aSJason M. Bills ret = sd_journal_get_data(journal, field.data(), (const void **)&data, 49*16428a1aSJason M. Bills &length); 50*16428a1aSJason M. Bills if (ret < 0) 51*16428a1aSJason M. Bills { 52*16428a1aSJason M. Bills return ret; 53*16428a1aSJason M. Bills } 54*16428a1aSJason M. Bills contents = boost::string_view(data, length); 55*16428a1aSJason M. Bills // Only use the content after the "=" character. 56*16428a1aSJason M. Bills contents.remove_prefix(std::min(contents.find("=") + 1, contents.size())); 57*16428a1aSJason M. Bills return ret; 58*16428a1aSJason M. Bills } 59*16428a1aSJason M. Bills 60*16428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal, 61*16428a1aSJason M. Bills const boost::string_view &field, const int &base, 62*16428a1aSJason M. Bills int &contents) 63*16428a1aSJason M. Bills { 64*16428a1aSJason M. Bills int ret = 0; 65*16428a1aSJason M. Bills boost::string_view metadata; 66*16428a1aSJason M. Bills // Get the metadata from the requested field of the journal entry 67*16428a1aSJason M. Bills ret = getJournalMetadata(journal, field, metadata); 68*16428a1aSJason M. Bills if (ret < 0) 69*16428a1aSJason M. Bills { 70*16428a1aSJason M. Bills return ret; 71*16428a1aSJason M. Bills } 72*16428a1aSJason M. Bills contents = strtol(metadata.data(), nullptr, base); 73*16428a1aSJason M. Bills return ret; 74*16428a1aSJason M. Bills } 75*16428a1aSJason M. Bills 76*16428a1aSJason M. Bills static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp) 77*16428a1aSJason M. Bills { 78*16428a1aSJason M. Bills int ret = 0; 79*16428a1aSJason M. Bills uint64_t timestamp = 0; 80*16428a1aSJason M. Bills ret = sd_journal_get_realtime_usec(journal, ×tamp); 81*16428a1aSJason M. Bills if (ret < 0) 82*16428a1aSJason M. Bills { 83*16428a1aSJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 84*16428a1aSJason M. Bills << strerror(-ret); 85*16428a1aSJason M. Bills return false; 86*16428a1aSJason M. Bills } 87*16428a1aSJason M. Bills time_t t = 88*16428a1aSJason M. Bills static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s 89*16428a1aSJason M. Bills struct tm *loctime = localtime(&t); 90*16428a1aSJason M. Bills char entryTime[64] = {}; 91*16428a1aSJason M. Bills if (NULL != loctime) 92*16428a1aSJason M. Bills { 93*16428a1aSJason M. Bills strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime); 94*16428a1aSJason M. Bills } 95*16428a1aSJason M. Bills // Insert the ':' into the timezone 96*16428a1aSJason M. Bills boost::string_view t1(entryTime); 97*16428a1aSJason M. Bills boost::string_view t2(entryTime); 98*16428a1aSJason M. Bills if (t1.size() > 2 && t2.size() > 2) 99*16428a1aSJason M. Bills { 100*16428a1aSJason M. Bills t1.remove_suffix(2); 101*16428a1aSJason M. Bills t2.remove_prefix(t2.size() - 2); 102*16428a1aSJason M. Bills } 103*16428a1aSJason M. Bills entryTimestamp = t1.to_string() + ":" + t2.to_string(); 104*16428a1aSJason M. Bills return true; 105*16428a1aSJason M. Bills } 106*16428a1aSJason M. Bills 107*16428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req, 108*16428a1aSJason M. Bills long &skip) 109*16428a1aSJason M. Bills { 110*16428a1aSJason M. Bills char *skipParam = req.urlParams.get("$skip"); 111*16428a1aSJason M. Bills if (skipParam != nullptr) 112*16428a1aSJason M. Bills { 113*16428a1aSJason M. Bills char *ptr = nullptr; 114*16428a1aSJason M. Bills skip = std::strtol(skipParam, &ptr, 10); 115*16428a1aSJason M. Bills if (*skipParam == '\0' || *ptr != '\0') 116*16428a1aSJason M. Bills { 117*16428a1aSJason M. Bills 118*16428a1aSJason M. Bills messages::queryParameterValueTypeError(res, std::string(skipParam), 119*16428a1aSJason M. Bills "$skip"); 120*16428a1aSJason M. Bills return false; 121*16428a1aSJason M. Bills } 122*16428a1aSJason M. Bills if (skip < 0) 123*16428a1aSJason M. Bills { 124*16428a1aSJason M. Bills 125*16428a1aSJason M. Bills messages::queryParameterOutOfRange(res, std::to_string(skip), 126*16428a1aSJason M. Bills "$skip", "greater than 0"); 127*16428a1aSJason M. Bills return false; 128*16428a1aSJason M. Bills } 129*16428a1aSJason M. Bills } 130*16428a1aSJason M. Bills return true; 131*16428a1aSJason M. Bills } 132*16428a1aSJason M. Bills 133*16428a1aSJason M. Bills static constexpr const long maxEntriesPerPage = 1000; 134*16428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req, 135*16428a1aSJason M. Bills long &top) 136*16428a1aSJason M. Bills { 137*16428a1aSJason M. Bills char *topParam = req.urlParams.get("$top"); 138*16428a1aSJason M. Bills if (topParam != nullptr) 139*16428a1aSJason M. Bills { 140*16428a1aSJason M. Bills char *ptr = nullptr; 141*16428a1aSJason M. Bills top = std::strtol(topParam, &ptr, 10); 142*16428a1aSJason M. Bills if (*topParam == '\0' || *ptr != '\0') 143*16428a1aSJason M. Bills { 144*16428a1aSJason M. Bills messages::queryParameterValueTypeError(res, std::string(topParam), 145*16428a1aSJason M. Bills "$top"); 146*16428a1aSJason M. Bills return false; 147*16428a1aSJason M. Bills } 148*16428a1aSJason M. Bills if (top < 1 || top > maxEntriesPerPage) 149*16428a1aSJason M. Bills { 150*16428a1aSJason M. Bills 151*16428a1aSJason M. Bills messages::queryParameterOutOfRange( 152*16428a1aSJason M. Bills res, std::to_string(top), "$top", 153*16428a1aSJason M. Bills "1-" + std::to_string(maxEntriesPerPage)); 154*16428a1aSJason M. Bills return false; 155*16428a1aSJason M. Bills } 156*16428a1aSJason M. Bills } 157*16428a1aSJason M. Bills return true; 158*16428a1aSJason M. Bills } 159*16428a1aSJason M. Bills 160*16428a1aSJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID) 161*16428a1aSJason M. Bills { 162*16428a1aSJason M. Bills int ret = 0; 163*16428a1aSJason M. Bills static uint64_t prevTs = 0; 164*16428a1aSJason M. Bills static int index = 0; 165*16428a1aSJason M. Bills // Get the entry timestamp 166*16428a1aSJason M. Bills uint64_t curTs = 0; 167*16428a1aSJason M. Bills ret = sd_journal_get_realtime_usec(journal, &curTs); 168*16428a1aSJason M. Bills if (ret < 0) 169*16428a1aSJason M. Bills { 170*16428a1aSJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 171*16428a1aSJason M. Bills << strerror(-ret); 172*16428a1aSJason M. Bills return false; 173*16428a1aSJason M. Bills } 174*16428a1aSJason M. Bills // If the timestamp isn't unique, increment the index 175*16428a1aSJason M. Bills if (curTs == prevTs) 176*16428a1aSJason M. Bills { 177*16428a1aSJason M. Bills index++; 178*16428a1aSJason M. Bills } 179*16428a1aSJason M. Bills else 180*16428a1aSJason M. Bills { 181*16428a1aSJason M. Bills // Otherwise, reset it 182*16428a1aSJason M. Bills index = 0; 183*16428a1aSJason M. Bills } 184*16428a1aSJason M. Bills // Save the timestamp 185*16428a1aSJason M. Bills prevTs = curTs; 186*16428a1aSJason M. Bills 187*16428a1aSJason M. Bills entryID = std::to_string(curTs); 188*16428a1aSJason M. Bills if (index > 0) 189*16428a1aSJason M. Bills { 190*16428a1aSJason M. Bills entryID += "_" + std::to_string(index); 191*16428a1aSJason M. Bills } 192*16428a1aSJason M. Bills return true; 193*16428a1aSJason M. Bills } 194*16428a1aSJason M. Bills 195*16428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID, 196*16428a1aSJason M. Bills uint64_t ×tamp, uint16_t &index) 197*16428a1aSJason M. Bills { 198*16428a1aSJason M. Bills if (entryID.empty()) 199*16428a1aSJason M. Bills { 200*16428a1aSJason M. Bills return false; 201*16428a1aSJason M. Bills } 202*16428a1aSJason M. Bills // Convert the unique ID back to a timestamp to find the entry 203*16428a1aSJason M. Bills boost::string_view tsStr(entryID); 204*16428a1aSJason M. Bills 205*16428a1aSJason M. Bills auto underscorePos = tsStr.find("_"); 206*16428a1aSJason M. Bills if (underscorePos != tsStr.npos) 207*16428a1aSJason M. Bills { 208*16428a1aSJason M. Bills // Timestamp has an index 209*16428a1aSJason M. Bills tsStr.remove_suffix(tsStr.size() - underscorePos); 210*16428a1aSJason M. Bills boost::string_view indexStr(entryID); 211*16428a1aSJason M. Bills indexStr.remove_prefix(underscorePos + 1); 212*16428a1aSJason M. Bills std::size_t pos; 213*16428a1aSJason M. Bills try 214*16428a1aSJason M. Bills { 215*16428a1aSJason M. Bills index = std::stoul(indexStr.to_string(), &pos); 216*16428a1aSJason M. Bills } 217*16428a1aSJason M. Bills catch (std::invalid_argument) 218*16428a1aSJason M. Bills { 219*16428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 220*16428a1aSJason M. Bills return false; 221*16428a1aSJason M. Bills } 222*16428a1aSJason M. Bills catch (std::out_of_range) 223*16428a1aSJason M. Bills { 224*16428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 225*16428a1aSJason M. Bills return false; 226*16428a1aSJason M. Bills } 227*16428a1aSJason M. Bills if (pos != indexStr.size()) 228*16428a1aSJason M. Bills { 229*16428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 230*16428a1aSJason M. Bills return false; 231*16428a1aSJason M. Bills } 232*16428a1aSJason M. Bills } 233*16428a1aSJason M. Bills // Timestamp has no index 234*16428a1aSJason M. Bills std::size_t pos; 235*16428a1aSJason M. Bills try 236*16428a1aSJason M. Bills { 237*16428a1aSJason M. Bills timestamp = std::stoull(tsStr.to_string(), &pos); 238*16428a1aSJason M. Bills } 239*16428a1aSJason M. Bills catch (std::invalid_argument) 240*16428a1aSJason M. Bills { 241*16428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 242*16428a1aSJason M. Bills return false; 243*16428a1aSJason M. Bills } 244*16428a1aSJason M. Bills catch (std::out_of_range) 245*16428a1aSJason M. Bills { 246*16428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 247*16428a1aSJason M. Bills return false; 248*16428a1aSJason M. Bills } 249*16428a1aSJason M. Bills if (pos != tsStr.size()) 250*16428a1aSJason M. Bills { 251*16428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 252*16428a1aSJason M. Bills return false; 253*16428a1aSJason M. Bills } 254*16428a1aSJason M. Bills return true; 255*16428a1aSJason M. Bills } 256*16428a1aSJason M. Bills 2571da66f75SEd Tanous class LogServiceCollection : public Node 2581da66f75SEd Tanous { 2591da66f75SEd Tanous public: 2601da66f75SEd Tanous template <typename CrowApp> 2611da66f75SEd Tanous LogServiceCollection(CrowApp &app) : 2624ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/") 2631da66f75SEd Tanous { 2641da66f75SEd Tanous // Collections use static ID for SubRoute to add to its parent, but only 2651da66f75SEd Tanous // load dynamic data so the duplicate static members don't get displayed 2664ed77cd5SEd Tanous Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices"; 2671da66f75SEd Tanous entityPrivileges = { 268e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 269e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 270e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 271e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 272e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 273e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 2741da66f75SEd Tanous } 2751da66f75SEd Tanous 2761da66f75SEd Tanous private: 2771da66f75SEd Tanous /** 2781da66f75SEd Tanous * Functions triggers appropriate requests on DBus 2791da66f75SEd Tanous */ 2801da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 2811da66f75SEd Tanous const std::vector<std::string> ¶ms) override 2821da66f75SEd Tanous { 283e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 2841da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 2851da66f75SEd Tanous // it has a duplicate entry for members 286e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 2871da66f75SEd Tanous "#LogServiceCollection.LogServiceCollection"; 288e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 2891da66f75SEd Tanous "/redfish/v1/" 2901da66f75SEd Tanous "$metadata#LogServiceCollection.LogServiceCollection"; 291e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 292e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices"; 293e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection"; 294e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 2951da66f75SEd Tanous "Collection of LogServices for this Manager"; 296e1f26343SJason M. Bills nlohmann::json &logserviceArray = asyncResp->res.jsonValue["Members"]; 2971da66f75SEd Tanous logserviceArray = nlohmann::json::array(); 298e1f26343SJason M. Bills logserviceArray.push_back( 299e1f26343SJason M. Bills {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog"}}); 3001da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG 3011da66f75SEd Tanous logserviceArray.push_back( 3024ed77cd5SEd Tanous {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/CpuLog"}}); 3031da66f75SEd Tanous #endif 304e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 305e1f26343SJason M. Bills logserviceArray.size(); 3061da66f75SEd Tanous } 3071da66f75SEd Tanous }; 3081da66f75SEd Tanous 309e1f26343SJason M. Bills class BMCLogService : public Node 3101da66f75SEd Tanous { 3111da66f75SEd Tanous public: 3121da66f75SEd Tanous template <typename CrowApp> 313e1f26343SJason M. Bills BMCLogService(CrowApp &app) : 314e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/") 315e1f26343SJason M. Bills { 316e1f26343SJason M. Bills // Set the id for SubRoute 317e1f26343SJason M. Bills Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/BmcLog"; 318e1f26343SJason M. Bills entityPrivileges = { 319e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 320e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 321e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 322e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 323e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 324e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 325e1f26343SJason M. Bills } 326e1f26343SJason M. Bills 327e1f26343SJason M. Bills private: 328e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 329e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 330e1f26343SJason M. Bills { 331e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 332e1f26343SJason M. Bills // Copy over the static data to include the entries added by SubRoute 333e1f26343SJason M. Bills asyncResp->res.jsonValue = Node::json; 334e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 335e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 336e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 337e1f26343SJason M. Bills "/redfish/v1/$metadata#LogService.LogService"; 338e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Log Service"; 339e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = "BMC Log Service"; 340e1f26343SJason M. Bills asyncResp->res.jsonValue["Id"] = "BMC Log"; 341e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 342e1f26343SJason M. Bills } 343e1f26343SJason M. Bills }; 344e1f26343SJason M. Bills 345e1f26343SJason M. Bills static int fillBMCLogEntryJson(const std::string &bmcLogEntryID, 346e1f26343SJason M. Bills sd_journal *journal, 347e1f26343SJason M. Bills nlohmann::json &bmcLogEntryJson) 348e1f26343SJason M. Bills { 349e1f26343SJason M. Bills // Get the Log Entry contents 350e1f26343SJason M. Bills int ret = 0; 351e1f26343SJason M. Bills 352*16428a1aSJason M. Bills boost::string_view msg; 353*16428a1aSJason M. Bills ret = getJournalMetadata(journal, "MESSAGE", msg); 354e1f26343SJason M. Bills if (ret < 0) 355e1f26343SJason M. Bills { 356e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret); 357e1f26343SJason M. Bills return 1; 358e1f26343SJason M. Bills } 359e1f26343SJason M. Bills 360e1f26343SJason M. Bills // Get the severity from the PRIORITY field 361e1f26343SJason M. Bills int severity = 8; // Default to an invalid priority 362*16428a1aSJason M. Bills ret = getJournalMetadata(journal, "PRIORITY", 10, severity); 363e1f26343SJason M. Bills if (ret < 0) 364e1f26343SJason M. Bills { 365e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret); 366e1f26343SJason M. Bills return 1; 367e1f26343SJason M. Bills } 368e1f26343SJason M. Bills 369e1f26343SJason M. Bills // Get the Created time from the timestamp 370*16428a1aSJason M. Bills std::string entryTimeStr; 371*16428a1aSJason M. Bills if (!getEntryTimestamp(journal, entryTimeStr)) 372e1f26343SJason M. Bills { 373*16428a1aSJason M. Bills return 1; 374e1f26343SJason M. Bills } 375e1f26343SJason M. Bills 376e1f26343SJason M. Bills // Fill in the log entry with the gathered data 377e1f26343SJason M. Bills bmcLogEntryJson = { 378e1f26343SJason M. Bills {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 379e1f26343SJason M. Bills {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 380e1f26343SJason M. Bills {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/" + 381e1f26343SJason M. Bills bmcLogEntryID}, 382e1f26343SJason M. Bills {"Name", "BMC Journal Entry"}, 383e1f26343SJason M. Bills {"Id", bmcLogEntryID}, 384*16428a1aSJason M. Bills {"Message", msg}, 385e1f26343SJason M. Bills {"EntryType", "Oem"}, 386e1f26343SJason M. Bills {"Severity", 387e1f26343SJason M. Bills severity <= 2 ? "Critical" 388e1f26343SJason M. Bills : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""}, 389e1f26343SJason M. Bills {"OemRecordFormat", "Intel BMC Journal Entry"}, 390e1f26343SJason M. Bills {"Created", std::move(entryTimeStr)}}; 391e1f26343SJason M. Bills return 0; 392e1f26343SJason M. Bills } 393e1f26343SJason M. Bills 394e1f26343SJason M. Bills class BMCLogEntryCollection : public Node 395e1f26343SJason M. Bills { 396e1f26343SJason M. Bills public: 397e1f26343SJason M. Bills template <typename CrowApp> 398e1f26343SJason M. Bills BMCLogEntryCollection(CrowApp &app) : 399e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/") 400e1f26343SJason M. Bills { 401e1f26343SJason M. Bills // Collections use static ID for SubRoute to add to its parent, but only 402e1f26343SJason M. Bills // load dynamic data so the duplicate static members don't get displayed 403e1f26343SJason M. Bills Node::json["@odata.id"] = 404e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; 405e1f26343SJason M. Bills entityPrivileges = { 406e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 407e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 408e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 409e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 410e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 411e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 412e1f26343SJason M. Bills } 413e1f26343SJason M. Bills 414e1f26343SJason M. Bills private: 415e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 416e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 417e1f26343SJason M. Bills { 418e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 419193ad2faSJason M. Bills static constexpr const long maxEntriesPerPage = 1000; 420193ad2faSJason M. Bills long skip = 0; 421193ad2faSJason M. Bills long top = maxEntriesPerPage; // Show max entries by default 422*16428a1aSJason M. Bills if (!getSkipParam(asyncResp->res, req, skip)) 423193ad2faSJason M. Bills { 424193ad2faSJason M. Bills return; 425193ad2faSJason M. Bills } 426*16428a1aSJason M. Bills if (!getTopParam(asyncResp->res, req, top)) 427193ad2faSJason M. Bills { 428193ad2faSJason M. Bills return; 429193ad2faSJason M. Bills } 430e1f26343SJason M. Bills // Collections don't include the static data added by SubRoute because 431e1f26343SJason M. Bills // it has a duplicate entry for members 432e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 433e1f26343SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 434e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 435e1f26343SJason M. Bills "/redfish/v1/" 436e1f26343SJason M. Bills "$metadata#LogEntryCollection.LogEntryCollection"; 437e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 438e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; 439e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; 440e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 441e1f26343SJason M. Bills "Collection of BMC Journal Entries"; 442e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 443e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 444e1f26343SJason M. Bills 445e1f26343SJason M. Bills // Go through the journal and use the timestamp to create a unique ID 446e1f26343SJason M. Bills // for each entry 447e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 448e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 449e1f26343SJason M. Bills if (ret < 0) 450e1f26343SJason M. Bills { 451e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 452f12894f8SJason M. Bills messages::internalError(asyncResp->res); 453e1f26343SJason M. Bills return; 454e1f26343SJason M. Bills } 455e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 456e1f26343SJason M. Bills journalTmp, sd_journal_close); 457e1f26343SJason M. Bills journalTmp = nullptr; 458193ad2faSJason M. Bills uint64_t entryCount = 0; 459e1f26343SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 460e1f26343SJason M. Bills { 461193ad2faSJason M. Bills entryCount++; 462193ad2faSJason M. Bills // Handle paging using skip (number of entries to skip from the 463193ad2faSJason M. Bills // start) and top (number of entries to display) 464193ad2faSJason M. Bills if (entryCount <= skip || entryCount > skip + top) 465193ad2faSJason M. Bills { 466193ad2faSJason M. Bills continue; 467193ad2faSJason M. Bills } 468193ad2faSJason M. Bills 469*16428a1aSJason M. Bills std::string idStr; 470*16428a1aSJason M. Bills if (!getUniqueEntryID(journal.get(), idStr)) 471e1f26343SJason M. Bills { 472e1f26343SJason M. Bills continue; 473e1f26343SJason M. Bills } 474e1f26343SJason M. Bills 475e1f26343SJason M. Bills logEntryArray.push_back({}); 476e1f26343SJason M. Bills nlohmann::json &bmcLogEntry = logEntryArray.back(); 477e1f26343SJason M. Bills if (fillBMCLogEntryJson(idStr, journal.get(), bmcLogEntry) != 0) 478e1f26343SJason M. Bills { 479f12894f8SJason M. Bills messages::internalError(asyncResp->res); 480e1f26343SJason M. Bills return; 481e1f26343SJason M. Bills } 482e1f26343SJason M. Bills } 483193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 484193ad2faSJason M. Bills if (skip + top < entryCount) 485193ad2faSJason M. Bills { 486193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 487193ad2faSJason M. Bills "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" + 488193ad2faSJason M. Bills std::to_string(skip + top); 489193ad2faSJason M. Bills } 490e1f26343SJason M. Bills } 491e1f26343SJason M. Bills }; 492e1f26343SJason M. Bills 493e1f26343SJason M. Bills class BMCLogEntry : public Node 494e1f26343SJason M. Bills { 495e1f26343SJason M. Bills public: 496e1f26343SJason M. Bills BMCLogEntry(CrowApp &app) : 497e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/<str>/", 498e1f26343SJason M. Bills std::string()) 499e1f26343SJason M. Bills { 500e1f26343SJason M. Bills entityPrivileges = { 501e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 502e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 503e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 504e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 505e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 506e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 507e1f26343SJason M. Bills } 508e1f26343SJason M. Bills 509e1f26343SJason M. Bills private: 510e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 511e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 512e1f26343SJason M. Bills { 513e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 514e1f26343SJason M. Bills if (params.size() != 1) 515e1f26343SJason M. Bills { 516f12894f8SJason M. Bills messages::internalError(asyncResp->res); 517e1f26343SJason M. Bills return; 518e1f26343SJason M. Bills } 519*16428a1aSJason M. Bills const std::string &entryID = params[0]; 520e1f26343SJason M. Bills // Convert the unique ID back to a timestamp to find the entry 521e1f26343SJason M. Bills uint64_t ts = 0; 522e1f26343SJason M. Bills uint16_t index = 0; 523*16428a1aSJason M. Bills if (!getTimestampFromID(asyncResp->res, entryID, ts, index)) 524e1f26343SJason M. Bills { 525*16428a1aSJason M. Bills return; 526e1f26343SJason M. Bills } 527e1f26343SJason M. Bills 528e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 529e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 530e1f26343SJason M. Bills if (ret < 0) 531e1f26343SJason M. Bills { 532e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 533f12894f8SJason M. Bills messages::internalError(asyncResp->res); 534e1f26343SJason M. Bills return; 535e1f26343SJason M. Bills } 536e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 537e1f26343SJason M. Bills journalTmp, sd_journal_close); 538e1f26343SJason M. Bills journalTmp = nullptr; 539e1f26343SJason M. Bills // Go to the timestamp in the log and move to the entry at the index 540e1f26343SJason M. Bills ret = sd_journal_seek_realtime_usec(journal.get(), ts); 541e1f26343SJason M. Bills for (int i = 0; i <= index; i++) 542e1f26343SJason M. Bills { 543e1f26343SJason M. Bills sd_journal_next(journal.get()); 544e1f26343SJason M. Bills } 545e1f26343SJason M. Bills if (fillBMCLogEntryJson(params[0], journal.get(), 546e1f26343SJason M. Bills asyncResp->res.jsonValue) != 0) 547e1f26343SJason M. Bills { 548f12894f8SJason M. Bills messages::internalError(asyncResp->res); 549e1f26343SJason M. Bills return; 550e1f26343SJason M. Bills } 551e1f26343SJason M. Bills } 552e1f26343SJason M. Bills }; 553e1f26343SJason M. Bills 554e1f26343SJason M. Bills class CPULogService : public Node 555e1f26343SJason M. Bills { 556e1f26343SJason M. Bills public: 557e1f26343SJason M. Bills template <typename CrowApp> 558e1f26343SJason M. Bills CPULogService(CrowApp &app) : 559e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/") 5601da66f75SEd Tanous { 5611da66f75SEd Tanous // Set the id for SubRoute 5624ed77cd5SEd Tanous Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/CpuLog"; 5631da66f75SEd Tanous entityPrivileges = { 564e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 565e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 566e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 567e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 568e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 569e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 5701da66f75SEd Tanous } 5711da66f75SEd Tanous 5721da66f75SEd Tanous private: 5731da66f75SEd Tanous /** 5741da66f75SEd Tanous * Functions triggers appropriate requests on DBus 5751da66f75SEd Tanous */ 5761da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 5771da66f75SEd Tanous const std::vector<std::string> ¶ms) override 5781da66f75SEd Tanous { 579e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 5801da66f75SEd Tanous // Copy over the static data to include the entries added by SubRoute 581e1f26343SJason M. Bills asyncResp->res.jsonValue = Node::json; 582e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 583e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 584e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 585e1f26343SJason M. Bills "/redfish/v1/" 5861da66f75SEd Tanous "$metadata#LogService.LogService"; 587e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service"; 588e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = "CPU Log Service"; 589e1f26343SJason M. Bills asyncResp->res.jsonValue["Id"] = "CPU Log"; 590e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 591e1f26343SJason M. Bills asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 592e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"] = { 5931da66f75SEd Tanous {"Oem", 5941da66f75SEd Tanous {{"#CpuLog.Immediate", 5951da66f75SEd Tanous {{"target", 5964ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 5971da66f75SEd Tanous "CpuLog.Immediate"}}}}}}; 5981da66f75SEd Tanous 5991da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI 600e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"]["Oem"].push_back( 6011da66f75SEd Tanous {"#CpuLog.SendRawPeci", 6021da66f75SEd Tanous {{"target", 6034ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 6041da66f75SEd Tanous "CpuLog.SendRawPeci"}}}); 6051da66f75SEd Tanous #endif 6061da66f75SEd Tanous } 6071da66f75SEd Tanous }; 6081da66f75SEd Tanous 609e1f26343SJason M. Bills class CPULogEntryCollection : public Node 6101da66f75SEd Tanous { 6111da66f75SEd Tanous public: 6121da66f75SEd Tanous template <typename CrowApp> 613e1f26343SJason M. Bills CPULogEntryCollection(CrowApp &app) : 614e1f26343SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/") 6151da66f75SEd Tanous { 6161da66f75SEd Tanous // Collections use static ID for SubRoute to add to its parent, but only 6171da66f75SEd Tanous // load dynamic data so the duplicate static members don't get displayed 6181da66f75SEd Tanous Node::json["@odata.id"] = 6194ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries"; 6201da66f75SEd Tanous entityPrivileges = { 621e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 622e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 623e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 624e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 625e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 626e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 6271da66f75SEd Tanous } 6281da66f75SEd Tanous 6291da66f75SEd Tanous private: 6301da66f75SEd Tanous /** 6311da66f75SEd Tanous * Functions triggers appropriate requests on DBus 6321da66f75SEd Tanous */ 6331da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 6341da66f75SEd Tanous const std::vector<std::string> ¶ms) override 6351da66f75SEd Tanous { 636e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 6371da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 6381da66f75SEd Tanous // it has a duplicate entry for members 639e1f26343SJason M. Bills auto getLogEntriesCallback = [asyncResp]( 640e1f26343SJason M. Bills const boost::system::error_code ec, 6411da66f75SEd Tanous const std::vector<std::string> &resp) { 6421da66f75SEd Tanous if (ec) 6431da66f75SEd Tanous { 6441da66f75SEd Tanous if (ec.value() != 6451da66f75SEd Tanous boost::system::errc::no_such_file_or_directory) 6461da66f75SEd Tanous { 6471da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to get entries ec: " 6481da66f75SEd Tanous << ec.message(); 649f12894f8SJason M. Bills messages::internalError(asyncResp->res); 6501da66f75SEd Tanous return; 6511da66f75SEd Tanous } 6521da66f75SEd Tanous } 653e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 6541da66f75SEd Tanous "#LogEntryCollection.LogEntryCollection"; 655e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.context"] = 6561da66f75SEd Tanous "/redfish/v1/" 6571da66f75SEd Tanous "$metadata#LogEntryCollection.LogEntryCollection"; 658e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 659e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries"; 660e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries"; 661e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 662e1f26343SJason M. Bills "Collection of CPU Log Entries"; 663e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 664e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 6651da66f75SEd Tanous for (const std::string &objpath : resp) 6661da66f75SEd Tanous { 6671da66f75SEd Tanous // Don't list the immediate log 6684ed77cd5SEd Tanous if (objpath.compare(cpuLogImmediatePath) == 0) 6691da66f75SEd Tanous { 6701da66f75SEd Tanous continue; 6711da66f75SEd Tanous } 6724ed77cd5SEd Tanous std::size_t lastPos = objpath.rfind("/"); 6734ed77cd5SEd Tanous if (lastPos != std::string::npos) 6741da66f75SEd Tanous { 675e1f26343SJason M. Bills logEntryArray.push_back( 676e1f26343SJason M. Bills {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/" 677e1f26343SJason M. Bills "CpuLog/Entries/" + 6784ed77cd5SEd Tanous objpath.substr(lastPos + 1)}}); 6791da66f75SEd Tanous } 6801da66f75SEd Tanous } 681e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 682e1f26343SJason M. Bills logEntryArray.size(); 6831da66f75SEd Tanous }; 6841da66f75SEd Tanous crow::connections::systemBus->async_method_call( 6851da66f75SEd Tanous std::move(getLogEntriesCallback), 6861da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", 6871da66f75SEd Tanous "/xyz/openbmc_project/object_mapper", 6881da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0, 6894ed77cd5SEd Tanous std::array<const char *, 1>{cpuLogInterface}); 6901da66f75SEd Tanous } 6911da66f75SEd Tanous }; 6921da66f75SEd Tanous 6931da66f75SEd Tanous std::string getLogCreatedTime(const nlohmann::json &cpuLog) 6941da66f75SEd Tanous { 6951da66f75SEd Tanous nlohmann::json::const_iterator metaIt = cpuLog.find("metadata"); 6961da66f75SEd Tanous if (metaIt != cpuLog.end()) 6971da66f75SEd Tanous { 6981da66f75SEd Tanous nlohmann::json::const_iterator tsIt = metaIt->find("timestamp"); 6991da66f75SEd Tanous if (tsIt != metaIt->end()) 7001da66f75SEd Tanous { 7011da66f75SEd Tanous const std::string *logTime = tsIt->get_ptr<const std::string *>(); 7021da66f75SEd Tanous if (logTime != nullptr) 7031da66f75SEd Tanous { 7041da66f75SEd Tanous return *logTime; 7051da66f75SEd Tanous } 7061da66f75SEd Tanous } 7071da66f75SEd Tanous } 7081da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to find log timestamp"; 7091da66f75SEd Tanous 7101da66f75SEd Tanous return std::string(); 7111da66f75SEd Tanous } 7121da66f75SEd Tanous 713e1f26343SJason M. Bills class CPULogEntry : public Node 7141da66f75SEd Tanous { 7151da66f75SEd Tanous public: 716e1f26343SJason M. Bills CPULogEntry(CrowApp &app) : 7174ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/", 7181da66f75SEd Tanous std::string()) 7191da66f75SEd Tanous { 7201da66f75SEd Tanous entityPrivileges = { 721e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 722e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 723e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 724e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 725e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 726e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 7271da66f75SEd Tanous } 7281da66f75SEd Tanous 7291da66f75SEd Tanous private: 7301da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 7311da66f75SEd Tanous const std::vector<std::string> ¶ms) override 7321da66f75SEd Tanous { 733e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 7341da66f75SEd Tanous if (params.size() != 1) 7351da66f75SEd Tanous { 736f12894f8SJason M. Bills messages::internalError(asyncResp->res); 7371da66f75SEd Tanous return; 7381da66f75SEd Tanous } 7394ed77cd5SEd Tanous const uint8_t logId = std::atoi(params[0].c_str()); 740e1f26343SJason M. Bills auto getStoredLogCallback = 741e1f26343SJason M. Bills [asyncResp, 7424ed77cd5SEd Tanous logId](const boost::system::error_code ec, 743e1f26343SJason M. Bills const sdbusplus::message::variant<std::string> &resp) { 7441da66f75SEd Tanous if (ec) 7451da66f75SEd Tanous { 746e1f26343SJason M. Bills BMCWEB_LOG_DEBUG << "failed to get log ec: " 747e1f26343SJason M. Bills << ec.message(); 748f12894f8SJason M. Bills messages::internalError(asyncResp->res); 7491da66f75SEd Tanous return; 7501da66f75SEd Tanous } 751e1f26343SJason M. Bills const std::string *log = 752e1f26343SJason M. Bills mapbox::getPtr<const std::string>(resp); 7531da66f75SEd Tanous if (log == nullptr) 7541da66f75SEd Tanous { 755f12894f8SJason M. Bills messages::internalError(asyncResp->res); 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 return; 7631da66f75SEd Tanous } 7641da66f75SEd Tanous std::string t = getLogCreatedTime(j); 765e1f26343SJason M. Bills asyncResp->res.jsonValue = { 7661da66f75SEd Tanous {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 767e1f26343SJason M. Bills {"@odata.context", 768e1f26343SJason M. Bills "/redfish/v1/$metadata#LogEntry.LogEntry"}, 7691da66f75SEd Tanous {"@odata.id", 7704ed77cd5SEd Tanous "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" + 7714ed77cd5SEd Tanous std::to_string(logId)}, 7721da66f75SEd Tanous {"Name", "CPU Debug Log"}, 7734ed77cd5SEd Tanous {"Id", logId}, 7741da66f75SEd Tanous {"EntryType", "Oem"}, 7751da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 7761da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 7771da66f75SEd Tanous {"Created", std::move(t)}}; 7781da66f75SEd Tanous }; 7791da66f75SEd Tanous crow::connections::systemBus->async_method_call( 7804ed77cd5SEd Tanous std::move(getStoredLogCallback), cpuLogObject, 7814ed77cd5SEd Tanous cpuLogPath + std::string("/") + std::to_string(logId), 7824ed77cd5SEd Tanous "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log"); 7831da66f75SEd Tanous } 7841da66f75SEd Tanous }; 7851da66f75SEd Tanous 786e1f26343SJason M. Bills class ImmediateCPULog : public Node 7871da66f75SEd Tanous { 7881da66f75SEd Tanous public: 789e1f26343SJason M. Bills ImmediateCPULog(CrowApp &app) : 7904ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 791e1f26343SJason M. Bills "CpuLog.Immediate/") 7921da66f75SEd Tanous { 7931da66f75SEd Tanous entityPrivileges = { 794e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 795e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 796e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 797e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 798e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 799e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 8001da66f75SEd Tanous } 8011da66f75SEd Tanous 8021da66f75SEd Tanous private: 8031da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 8041da66f75SEd Tanous const std::vector<std::string> ¶ms) override 8051da66f75SEd Tanous { 806e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 8071da66f75SEd Tanous static std::unique_ptr<sdbusplus::bus::match::match> 8081da66f75SEd Tanous immediateLogMatcher; 8091da66f75SEd Tanous 8101da66f75SEd Tanous // Only allow one Immediate Log request at a time 8111da66f75SEd Tanous if (immediateLogMatcher != nullptr) 8121da66f75SEd Tanous { 813e1f26343SJason M. Bills asyncResp->res.addHeader("Retry-After", "30"); 814f12894f8SJason M. Bills messages::serviceTemporarilyUnavailable(asyncResp->res, "30"); 8151da66f75SEd Tanous return; 8161da66f75SEd Tanous } 8171da66f75SEd Tanous // Make this static so it survives outside this method 8181da66f75SEd Tanous static boost::asio::deadline_timer timeout(*req.ioService); 8191da66f75SEd Tanous 8201da66f75SEd Tanous timeout.expires_from_now(boost::posix_time::seconds(30)); 821e1f26343SJason M. Bills timeout.async_wait([asyncResp](const boost::system::error_code &ec) { 8221da66f75SEd Tanous immediateLogMatcher = nullptr; 8231da66f75SEd Tanous if (ec) 8241da66f75SEd Tanous { 8251da66f75SEd Tanous // operation_aborted is expected if timer is canceled before 8261da66f75SEd Tanous // completion. 8271da66f75SEd Tanous if (ec != boost::asio::error::operation_aborted) 8281da66f75SEd Tanous { 8291da66f75SEd Tanous BMCWEB_LOG_ERROR << "Async_wait failed " << ec; 8301da66f75SEd Tanous } 8311da66f75SEd Tanous return; 8321da66f75SEd Tanous } 8331da66f75SEd Tanous BMCWEB_LOG_ERROR << "Timed out waiting for immediate log"; 8341da66f75SEd Tanous 835f12894f8SJason M. Bills messages::internalError(asyncResp->res); 8361da66f75SEd Tanous }); 8371da66f75SEd Tanous 838e1f26343SJason M. Bills auto immediateLogMatcherCallback = [asyncResp]( 8391da66f75SEd Tanous sdbusplus::message::message &m) { 8401da66f75SEd Tanous BMCWEB_LOG_DEBUG << "Immediate log available match fired"; 8411da66f75SEd Tanous boost::system::error_code ec; 8421da66f75SEd Tanous timeout.cancel(ec); 8431da66f75SEd Tanous if (ec) 8441da66f75SEd Tanous { 8451da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " << ec; 8461da66f75SEd Tanous } 8474ed77cd5SEd Tanous sdbusplus::message::object_path objPath; 8481da66f75SEd Tanous boost::container::flat_map< 8491da66f75SEd Tanous std::string, 8501da66f75SEd Tanous boost::container::flat_map< 8511da66f75SEd Tanous std::string, sdbusplus::message::variant<std::string>>> 8524ed77cd5SEd Tanous interfacesAdded; 8534ed77cd5SEd Tanous m.read(objPath, interfacesAdded); 8541da66f75SEd Tanous const std::string *log = mapbox::getPtr<const std::string>( 8554ed77cd5SEd Tanous interfacesAdded[cpuLogInterface]["Log"]); 8561da66f75SEd Tanous if (log == nullptr) 8571da66f75SEd Tanous { 858f12894f8SJason M. Bills messages::internalError(asyncResp->res); 8591da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 8601da66f75SEd Tanous // match object inside which this lambda is executing. Once it 8611da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 8621da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 8631da66f75SEd Tanous // be the last thing done. 8641da66f75SEd Tanous immediateLogMatcher = nullptr; 8651da66f75SEd Tanous return; 8661da66f75SEd Tanous } 8671da66f75SEd Tanous nlohmann::json j = nlohmann::json::parse(*log, nullptr, false); 8681da66f75SEd Tanous if (j.is_discarded()) 8691da66f75SEd Tanous { 870f12894f8SJason M. Bills messages::internalError(asyncResp->res); 8711da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 8721da66f75SEd Tanous // match object inside which this lambda is executing. Once it 8731da66f75SEd Tanous // is set to nullptr, the match object will be destroyed and the 8741da66f75SEd Tanous // lambda will lose its context, including res, so it needs to 8751da66f75SEd Tanous // be the last thing done. 8761da66f75SEd Tanous immediateLogMatcher = nullptr; 8771da66f75SEd Tanous return; 8781da66f75SEd Tanous } 8791da66f75SEd Tanous std::string t = getLogCreatedTime(j); 880e1f26343SJason M. Bills asyncResp->res.jsonValue = { 8811da66f75SEd Tanous {"@odata.type", "#LogEntry.v1_3_0.LogEntry"}, 8821da66f75SEd Tanous {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 8831da66f75SEd Tanous {"Name", "CPU Debug Log"}, 8841da66f75SEd Tanous {"EntryType", "Oem"}, 8851da66f75SEd Tanous {"OemRecordFormat", "Intel CPU Log"}, 8861da66f75SEd Tanous {"Oem", {{"Intel", std::move(j)}}}, 8871da66f75SEd Tanous {"Created", std::move(t)}}; 8881da66f75SEd Tanous // Careful with immediateLogMatcher. It is a unique_ptr to the 8891da66f75SEd Tanous // match object inside which this lambda is executing. Once it is 8901da66f75SEd Tanous // set to nullptr, the match object will be destroyed and the lambda 8911da66f75SEd Tanous // will lose its context, including res, so it needs to be the last 8921da66f75SEd Tanous // thing done. 8931da66f75SEd Tanous immediateLogMatcher = nullptr; 8941da66f75SEd Tanous }; 8951da66f75SEd Tanous immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>( 8961da66f75SEd Tanous *crow::connections::systemBus, 8971da66f75SEd Tanous sdbusplus::bus::match::rules::interfacesAdded() + 8984ed77cd5SEd Tanous sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath), 8991da66f75SEd Tanous std::move(immediateLogMatcherCallback)); 9001da66f75SEd Tanous 9011da66f75SEd Tanous auto generateImmediateLogCallback = 902e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 9031da66f75SEd Tanous const std::string &resp) { 9041da66f75SEd Tanous if (ec) 9051da66f75SEd Tanous { 9061da66f75SEd Tanous if (ec.value() == 9071da66f75SEd Tanous boost::system::errc::operation_not_supported) 9081da66f75SEd Tanous { 909f12894f8SJason M. Bills messages::resourceInStandby(asyncResp->res); 9101da66f75SEd Tanous } 9111da66f75SEd Tanous else 9121da66f75SEd Tanous { 913f12894f8SJason M. Bills messages::internalError(asyncResp->res); 9141da66f75SEd Tanous } 9151da66f75SEd Tanous boost::system::error_code timeoutec; 9161da66f75SEd Tanous timeout.cancel(timeoutec); 9171da66f75SEd Tanous if (timeoutec) 9181da66f75SEd Tanous { 9191da66f75SEd Tanous BMCWEB_LOG_ERROR << "error canceling timer " 9201da66f75SEd Tanous << timeoutec; 9211da66f75SEd Tanous } 9221da66f75SEd Tanous immediateLogMatcher = nullptr; 9231da66f75SEd Tanous return; 9241da66f75SEd Tanous } 9251da66f75SEd Tanous }; 9261da66f75SEd Tanous crow::connections::systemBus->async_method_call( 9274ed77cd5SEd Tanous std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath, 9284ed77cd5SEd Tanous cpuLogImmediateInterface, "GenerateImmediateLog"); 9291da66f75SEd Tanous } 9301da66f75SEd Tanous }; 9311da66f75SEd Tanous 932e1f26343SJason M. Bills class SendRawPECI : public Node 9331da66f75SEd Tanous { 9341da66f75SEd Tanous public: 935e1f26343SJason M. Bills SendRawPECI(CrowApp &app) : 9364ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/" 937e1f26343SJason M. Bills "CpuLog.SendRawPeci/") 9381da66f75SEd Tanous { 9391da66f75SEd Tanous entityPrivileges = { 9401da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 9411da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 9421da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 9431da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 9441da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 9451da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 9461da66f75SEd Tanous } 9471da66f75SEd Tanous 9481da66f75SEd Tanous private: 9491da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 9501da66f75SEd Tanous const std::vector<std::string> ¶ms) override 9511da66f75SEd Tanous { 952e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 9531da66f75SEd Tanous // Get the Raw PECI command from the request 954e1f26343SJason M. Bills nlohmann::json rawPECICmd; 955e1f26343SJason M. Bills if (!json_util::processJsonFromRequest(res, req, rawPECICmd)) 9561da66f75SEd Tanous { 9571da66f75SEd Tanous return; 9581da66f75SEd Tanous } 9591da66f75SEd Tanous // Get the Client Address from the request 960e1f26343SJason M. Bills nlohmann::json::const_iterator caIt = rawPECICmd.find("ClientAddress"); 961e1f26343SJason M. Bills if (caIt == rawPECICmd.end()) 9621da66f75SEd Tanous { 963a08b46ccSJason M. Bills messages::propertyMissing(asyncResp->res, "ClientAddress"); 9641da66f75SEd Tanous return; 9651da66f75SEd Tanous } 9661da66f75SEd Tanous const uint64_t *ca = caIt->get_ptr<const uint64_t *>(); 9671da66f75SEd Tanous if (ca == nullptr) 9681da66f75SEd Tanous { 969f12894f8SJason M. Bills messages::propertyValueTypeError(asyncResp->res, caIt->dump(), 970a08b46ccSJason M. Bills "ClientAddress"); 9711da66f75SEd Tanous return; 9721da66f75SEd Tanous } 9731da66f75SEd Tanous // Get the Read Length from the request 9741da66f75SEd Tanous const uint8_t clientAddress = static_cast<uint8_t>(*ca); 975e1f26343SJason M. Bills nlohmann::json::const_iterator rlIt = rawPECICmd.find("ReadLength"); 976e1f26343SJason M. Bills if (rlIt == rawPECICmd.end()) 9771da66f75SEd Tanous { 978a08b46ccSJason M. Bills messages::propertyMissing(asyncResp->res, "ReadLength"); 9791da66f75SEd Tanous return; 9801da66f75SEd Tanous } 9811da66f75SEd Tanous const uint64_t *rl = rlIt->get_ptr<const uint64_t *>(); 9821da66f75SEd Tanous if (rl == nullptr) 9831da66f75SEd Tanous { 984f12894f8SJason M. Bills messages::propertyValueTypeError(asyncResp->res, rlIt->dump(), 985a08b46ccSJason M. Bills "ReadLength"); 9861da66f75SEd Tanous return; 9871da66f75SEd Tanous } 9881da66f75SEd Tanous // Get the PECI Command from the request 9891da66f75SEd Tanous const uint32_t readLength = static_cast<uint32_t>(*rl); 990e1f26343SJason M. Bills nlohmann::json::const_iterator pcIt = rawPECICmd.find("PECICommand"); 991e1f26343SJason M. Bills if (pcIt == rawPECICmd.end()) 9921da66f75SEd Tanous { 993a08b46ccSJason M. Bills messages::propertyMissing(asyncResp->res, "PECICommand"); 9941da66f75SEd Tanous return; 9951da66f75SEd Tanous } 9961da66f75SEd Tanous std::vector<uint8_t> peciCommand; 9971da66f75SEd Tanous for (auto pc : *pcIt) 9981da66f75SEd Tanous { 9991da66f75SEd Tanous const uint64_t *val = pc.get_ptr<const uint64_t *>(); 10001da66f75SEd Tanous if (val == nullptr) 10011da66f75SEd Tanous { 10021da66f75SEd Tanous messages::propertyValueTypeError( 1003f12894f8SJason M. Bills asyncResp->res, pc.dump(), 1004a08b46ccSJason M. Bills "PECICommand/" + std::to_string(peciCommand.size())); 10051da66f75SEd Tanous return; 10061da66f75SEd Tanous } 10071da66f75SEd Tanous peciCommand.push_back(static_cast<uint8_t>(*val)); 10081da66f75SEd Tanous } 10091da66f75SEd Tanous // Callback to return the Raw PECI response 1010e1f26343SJason M. Bills auto sendRawPECICallback = 1011e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 10121da66f75SEd Tanous const std::vector<uint8_t> &resp) { 10131da66f75SEd Tanous if (ec) 10141da66f75SEd Tanous { 10151da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to send PECI command ec: " 10161da66f75SEd Tanous << ec.message(); 1017f12894f8SJason M. Bills messages::internalError(asyncResp->res); 10181da66f75SEd Tanous return; 10191da66f75SEd Tanous } 1020e1f26343SJason M. Bills asyncResp->res.jsonValue = {{"Name", "PECI Command Response"}, 10211da66f75SEd Tanous {"PECIResponse", resp}}; 10221da66f75SEd Tanous }; 10231da66f75SEd Tanous // Call the SendRawPECI command with the provided data 10241da66f75SEd Tanous crow::connections::systemBus->async_method_call( 1025e1f26343SJason M. Bills std::move(sendRawPECICallback), cpuLogObject, cpuLogPath, 1026e1f26343SJason M. Bills cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength, 10274ed77cd5SEd Tanous peciCommand); 10281da66f75SEd Tanous } 10291da66f75SEd Tanous }; 10301da66f75SEd Tanous 10311da66f75SEd Tanous } // namespace redfish 1032