1 #pragma once 2 #include <app.h> 3 #include <tinyxml2.h> 4 5 #include <async_resp.hpp> 6 #include <boost/algorithm/string.hpp> 7 #include <boost/container/flat_set.hpp> 8 #include <error_messages.hpp> 9 #include <ibm/locks.hpp> 10 #include <nlohmann/json.hpp> 11 #include <sdbusplus/message/types.hpp> 12 #include <utils/json_utils.hpp> 13 14 #include <filesystem> 15 #include <fstream> 16 #include <regex> 17 18 // Allow save area file size to 500KB 19 #define MAX_SAVE_AREA_FILESIZE 500000 20 21 using SType = std::string; 22 using SegmentFlags = std::vector<std::pair<std::string, uint32_t>>; 23 using LockRequest = std::tuple<SType, SType, SType, uint64_t, SegmentFlags>; 24 using LockRequests = std::vector<LockRequest>; 25 using Rc = std::pair<bool, std::variant<uint32_t, LockRequest>>; 26 using RcGetLockList = 27 std::variant<std::string, std::vector<std::pair<uint32_t, LockRequests>>>; 28 using ListOfSessionIds = std::vector<std::string>; 29 namespace crow 30 { 31 namespace ibm_mc 32 { 33 constexpr const char* methodNotAllowedMsg = "Method Not Allowed"; 34 constexpr const char* resourceNotFoundMsg = "Resource Not Found"; 35 constexpr const char* contentNotAcceptableMsg = "Content Not Acceptable"; 36 constexpr const char* internalServerError = "Internal Server Error"; 37 38 bool createSaveAreaPath(crow::Response& res) 39 { 40 // The path /var/lib/obmc will be created by initrdscripts 41 // Create the directories for the save-area files, when we get 42 // first file upload request 43 std::error_code ec; 44 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt", ec)) 45 { 46 std::filesystem::create_directory("/var/lib/obmc/bmc-console-mgmt", ec); 47 } 48 if (ec) 49 { 50 res.result(boost::beast::http::status::internal_server_error); 51 res.jsonValue["Description"] = internalServerError; 52 BMCWEB_LOG_DEBUG 53 << "handleIbmPost: Failed to prepare save-area directory. ec : " 54 << ec; 55 return false; 56 } 57 58 if (!std::filesystem::is_directory( 59 "/var/lib/obmc/bmc-console-mgmt/save-area", ec)) 60 { 61 std::filesystem::create_directory( 62 "/var/lib/obmc/bmc-console-mgmt/save-area", ec); 63 } 64 if (ec) 65 { 66 res.result(boost::beast::http::status::internal_server_error); 67 res.jsonValue["Description"] = internalServerError; 68 BMCWEB_LOG_DEBUG 69 << "handleIbmPost: Failed to prepare save-area directory. ec : " 70 << ec; 71 return false; 72 } 73 return true; 74 } 75 void handleFilePut(const crow::Request& req, crow::Response& res, 76 const std::string& fileID) 77 { 78 // Check the content-type of the request 79 std::string_view contentType = req.getHeaderValue("content-type"); 80 if (boost::starts_with(contentType, "multipart/form-data")) 81 { 82 BMCWEB_LOG_DEBUG 83 << "This is multipart/form-data. Invalid content for PUT"; 84 85 res.result(boost::beast::http::status::not_acceptable); 86 res.jsonValue["Description"] = contentNotAcceptableMsg; 87 return; 88 } 89 else 90 { 91 BMCWEB_LOG_DEBUG << "Not a multipart/form-data. Continue.."; 92 } 93 94 BMCWEB_LOG_DEBUG 95 << "handleIbmPut: Request to create/update the save-area file"; 96 if (!createSaveAreaPath(res)) 97 { 98 res.result(boost::beast::http::status::not_found); 99 res.jsonValue["Description"] = resourceNotFoundMsg; 100 return; 101 } 102 // Create the file 103 std::ofstream file; 104 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area"); 105 loc /= fileID; 106 107 std::string data = std::move(req.body); 108 BMCWEB_LOG_DEBUG << "data capaticty : " << data.capacity(); 109 if (data.capacity() > MAX_SAVE_AREA_FILESIZE) 110 { 111 res.result(boost::beast::http::status::bad_request); 112 res.jsonValue["Description"] = 113 "File size exceeds maximum allowed size[500KB]"; 114 return; 115 } 116 BMCWEB_LOG_DEBUG << "Creating file " << loc; 117 file.open(loc, std::ofstream::out); 118 if (file.fail()) 119 { 120 BMCWEB_LOG_DEBUG << "Error while opening the file for writing"; 121 res.result(boost::beast::http::status::internal_server_error); 122 res.jsonValue["Description"] = "Error while creating the file"; 123 return; 124 } 125 else 126 { 127 file << data; 128 BMCWEB_LOG_DEBUG << "save-area file is created"; 129 res.jsonValue["Description"] = "File Created"; 130 } 131 } 132 133 void handleConfigFileList(crow::Response& res) 134 { 135 std::vector<std::string> pathObjList; 136 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area"); 137 if (std::filesystem::exists(loc) && std::filesystem::is_directory(loc)) 138 { 139 for (const auto& file : std::filesystem::directory_iterator(loc)) 140 { 141 std::filesystem::path pathObj(file.path()); 142 pathObjList.push_back("/ibm/v1/Host/ConfigFiles/" + 143 pathObj.filename().string()); 144 } 145 } 146 res.jsonValue["@odata.type"] = "#FileCollection.v1_0_0.FileCollection"; 147 res.jsonValue["@odata.id"] = "/ibm/v1/Host/ConfigFiles/"; 148 res.jsonValue["Id"] = "ConfigFiles"; 149 res.jsonValue["Name"] = "ConfigFiles"; 150 151 res.jsonValue["Members"] = std::move(pathObjList); 152 res.jsonValue["Actions"]["#FileCollection.DeleteAll"] = { 153 {"target", 154 "/ibm/v1/Host/ConfigFiles/Actions/FileCollection.DeleteAll"}}; 155 res.end(); 156 } 157 158 void deleteConfigFiles(crow::Response& res) 159 { 160 std::vector<std::string> pathObjList; 161 std::error_code ec; 162 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area"); 163 if (std::filesystem::exists(loc) && std::filesystem::is_directory(loc)) 164 { 165 std::filesystem::remove_all(loc, ec); 166 if (ec) 167 { 168 res.result(boost::beast::http::status::internal_server_error); 169 res.jsonValue["Description"] = internalServerError; 170 BMCWEB_LOG_DEBUG << "deleteConfigFiles: Failed to delete the " 171 "config files directory. ec : " 172 << ec; 173 } 174 } 175 res.end(); 176 } 177 178 void getLockServiceData(crow::Response& res) 179 { 180 res.jsonValue["@odata.type"] = "#LockService.v1_0_0.LockService"; 181 res.jsonValue["@odata.id"] = "/ibm/v1/HMC/LockService/"; 182 res.jsonValue["Id"] = "LockService"; 183 res.jsonValue["Name"] = "LockService"; 184 185 res.jsonValue["Actions"]["#LockService.AcquireLock"] = { 186 {"target", "/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock"}}; 187 res.jsonValue["Actions"]["#LockService.ReleaseLock"] = { 188 {"target", "/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock"}}; 189 res.jsonValue["Actions"]["#LockService.GetLockList"] = { 190 {"target", "/ibm/v1/HMC/LockService/Actions/LockService.GetLockList"}}; 191 res.end(); 192 } 193 194 void handleFileGet(crow::Response& res, const std::string& fileID) 195 { 196 BMCWEB_LOG_DEBUG << "HandleGet on SaveArea files on path: " << fileID; 197 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area/" + 198 fileID); 199 if (!std::filesystem::exists(loc)) 200 { 201 BMCWEB_LOG_ERROR << loc << "Not found"; 202 res.result(boost::beast::http::status::not_found); 203 res.jsonValue["Description"] = resourceNotFoundMsg; 204 return; 205 } 206 207 std::ifstream readfile(loc.string()); 208 if (!readfile) 209 { 210 BMCWEB_LOG_ERROR << loc.string() << "Not found"; 211 res.result(boost::beast::http::status::not_found); 212 res.jsonValue["Description"] = resourceNotFoundMsg; 213 return; 214 } 215 216 std::string contentDispositionParam = 217 "attachment; filename=\"" + fileID + "\""; 218 res.addHeader("Content-Disposition", contentDispositionParam); 219 std::string fileData; 220 fileData = {std::istreambuf_iterator<char>(readfile), 221 std::istreambuf_iterator<char>()}; 222 res.jsonValue["Data"] = fileData; 223 return; 224 } 225 226 void handleFileDelete(crow::Response& res, const std::string& fileID) 227 { 228 std::string filePath("/var/lib/obmc/bmc-console-mgmt/save-area/" + fileID); 229 BMCWEB_LOG_DEBUG << "Removing the file : " << filePath << "\n"; 230 231 std::ifstream file_open(filePath.c_str()); 232 if (static_cast<bool>(file_open)) 233 if (remove(filePath.c_str()) == 0) 234 { 235 BMCWEB_LOG_DEBUG << "File removed!\n"; 236 res.jsonValue["Description"] = "File Deleted"; 237 } 238 else 239 { 240 BMCWEB_LOG_ERROR << "File not removed!\n"; 241 res.result(boost::beast::http::status::internal_server_error); 242 res.jsonValue["Description"] = internalServerError; 243 } 244 else 245 { 246 BMCWEB_LOG_ERROR << "File not found!\n"; 247 res.result(boost::beast::http::status::not_found); 248 res.jsonValue["Description"] = resourceNotFoundMsg; 249 } 250 return; 251 } 252 253 inline void handleFileUrl(const crow::Request& req, crow::Response& res, 254 const std::string& fileID) 255 { 256 if (req.method() == "PUT"_method) 257 { 258 handleFilePut(req, res, fileID); 259 res.end(); 260 return; 261 } 262 if (req.method() == "GET"_method) 263 { 264 handleFileGet(res, fileID); 265 res.end(); 266 return; 267 } 268 if (req.method() == "DELETE"_method) 269 { 270 handleFileDelete(res, fileID); 271 res.end(); 272 return; 273 } 274 } 275 276 void handleAcquireLockAPI(const crow::Request& req, crow::Response& res, 277 std::vector<nlohmann::json> body) 278 { 279 LockRequests lockRequestStructure; 280 for (auto& element : body) 281 { 282 std::string lockType; 283 uint64_t resourceId; 284 285 SegmentFlags segInfo; 286 std::vector<nlohmann::json> segmentFlags; 287 288 if (!redfish::json_util::readJson(element, res, "LockType", lockType, 289 "ResourceID", resourceId, 290 "SegmentFlags", segmentFlags)) 291 { 292 BMCWEB_LOG_DEBUG << "Not a Valid JSON"; 293 res.result(boost::beast::http::status::bad_request); 294 res.end(); 295 return; 296 } 297 BMCWEB_LOG_DEBUG << lockType; 298 BMCWEB_LOG_DEBUG << resourceId; 299 300 BMCWEB_LOG_DEBUG << "Segment Flags are present"; 301 302 for (auto& e : segmentFlags) 303 { 304 std::string lockFlags; 305 uint32_t segmentLength; 306 307 if (!redfish::json_util::readJson(e, res, "LockFlag", lockFlags, 308 "SegmentLength", segmentLength)) 309 { 310 res.result(boost::beast::http::status::bad_request); 311 res.end(); 312 return; 313 } 314 315 BMCWEB_LOG_DEBUG << "Lockflag : " << lockFlags; 316 BMCWEB_LOG_DEBUG << "SegmentLength : " << segmentLength; 317 318 segInfo.push_back(std::make_pair(lockFlags, segmentLength)); 319 } 320 lockRequestStructure.push_back(make_tuple( 321 req.session->uniqueId, "hmc-id", lockType, resourceId, segInfo)); 322 } 323 324 // print lock request into journal 325 326 for (uint32_t i = 0; i < lockRequestStructure.size(); i++) 327 { 328 BMCWEB_LOG_DEBUG << std::get<0>(lockRequestStructure[i]); 329 BMCWEB_LOG_DEBUG << std::get<1>(lockRequestStructure[i]); 330 BMCWEB_LOG_DEBUG << std::get<2>(lockRequestStructure[i]); 331 BMCWEB_LOG_DEBUG << std::get<3>(lockRequestStructure[i]); 332 333 for (const auto& p : std::get<4>(lockRequestStructure[i])) 334 { 335 BMCWEB_LOG_DEBUG << p.first << ", " << p.second; 336 } 337 } 338 339 const LockRequests& t = lockRequestStructure; 340 341 auto varAcquireLock = crow::ibm_mc_lock::Lock::getInstance().acquireLock(t); 342 343 if (varAcquireLock.first) 344 { 345 // Either validity failure of there is a conflict with itself 346 347 auto validityStatus = 348 std::get<std::pair<bool, int>>(varAcquireLock.second); 349 350 if ((!validityStatus.first) && (validityStatus.second == 0)) 351 { 352 BMCWEB_LOG_DEBUG << "Not a Valid record"; 353 BMCWEB_LOG_DEBUG << "Bad json in request"; 354 res.result(boost::beast::http::status::bad_request); 355 res.end(); 356 return; 357 } 358 if (validityStatus.first && (validityStatus.second == 1)) 359 { 360 BMCWEB_LOG_DEBUG << "There is a conflict within itself"; 361 res.result(boost::beast::http::status::bad_request); 362 res.end(); 363 return; 364 } 365 } 366 else 367 { 368 auto conflictStatus = 369 std::get<crow::ibm_mc_lock::Rc>(varAcquireLock.second); 370 if (!conflictStatus.first) 371 { 372 BMCWEB_LOG_DEBUG << "There is no conflict with the locktable"; 373 res.result(boost::beast::http::status::ok); 374 375 auto var = std::get<uint32_t>(conflictStatus.second); 376 nlohmann::json returnJson; 377 returnJson["id"] = var; 378 res.jsonValue["TransactionID"] = var; 379 res.end(); 380 return; 381 } 382 else 383 { 384 BMCWEB_LOG_DEBUG << "There is a conflict with the lock table"; 385 res.result(boost::beast::http::status::conflict); 386 auto var = std::get<std::pair<uint32_t, LockRequest>>( 387 conflictStatus.second); 388 nlohmann::json returnJson, segments; 389 nlohmann::json myarray = nlohmann::json::array(); 390 returnJson["TransactionID"] = var.first; 391 returnJson["SessionID"] = std::get<0>(var.second); 392 returnJson["HMCID"] = std::get<1>(var.second); 393 returnJson["LockType"] = std::get<2>(var.second); 394 returnJson["ResourceID"] = std::get<3>(var.second); 395 396 for (uint32_t i = 0; i < std::get<4>(var.second).size(); i++) 397 { 398 segments["LockFlag"] = std::get<4>(var.second)[i].first; 399 segments["SegmentLength"] = std::get<4>(var.second)[i].second; 400 myarray.push_back(segments); 401 } 402 403 returnJson["SegmentFlags"] = myarray; 404 405 res.jsonValue["Record"] = returnJson; 406 res.end(); 407 return; 408 } 409 } 410 } 411 void handleRelaseAllAPI(const crow::Request& req, crow::Response& res) 412 { 413 crow::ibm_mc_lock::Lock::getInstance().releaseLock(req.session->uniqueId); 414 res.result(boost::beast::http::status::ok); 415 res.end(); 416 return; 417 } 418 419 void handleReleaseLockAPI(const crow::Request& req, crow::Response& res, 420 const std::vector<uint32_t>& listTransactionIds) 421 { 422 BMCWEB_LOG_DEBUG << listTransactionIds.size(); 423 BMCWEB_LOG_DEBUG << "Data is present"; 424 for (uint32_t i = 0; i < listTransactionIds.size(); i++) 425 { 426 BMCWEB_LOG_DEBUG << listTransactionIds[i]; 427 } 428 429 std::string clientId = "hmc-id"; 430 std::string sessionId = req.session->uniqueId; 431 432 // validate the request ids 433 434 auto varReleaselock = crow::ibm_mc_lock::Lock::getInstance().releaseLock( 435 listTransactionIds, std::make_pair(clientId, sessionId)); 436 437 if (!varReleaselock.first) 438 { 439 // validation Failed 440 res.result(boost::beast::http::status::bad_request); 441 res.end(); 442 return; 443 } 444 else 445 { 446 auto statusRelease = 447 std::get<crow::ibm_mc_lock::RcRelaseLock>(varReleaselock.second); 448 if (statusRelease.first) 449 { 450 // The current hmc owns all the locks, so we already released 451 // them 452 res.result(boost::beast::http::status::ok); 453 res.end(); 454 return; 455 } 456 457 else 458 { 459 // valid rid, but the current hmc does not own all the locks 460 BMCWEB_LOG_DEBUG << "Current HMC does not own all the locks"; 461 res.result(boost::beast::http::status::unauthorized); 462 463 auto var = statusRelease.second; 464 nlohmann::json returnJson, segments; 465 nlohmann::json myArray = nlohmann::json::array(); 466 returnJson["TransactionID"] = var.first; 467 returnJson["SessionID"] = std::get<0>(var.second); 468 returnJson["HMCID"] = std::get<1>(var.second); 469 returnJson["LockType"] = std::get<2>(var.second); 470 returnJson["ResourceID"] = std::get<3>(var.second); 471 472 for (uint32_t i = 0; i < std::get<4>(var.second).size(); i++) 473 { 474 segments["LockFlag"] = std::get<4>(var.second)[i].first; 475 segments["SegmentLength"] = std::get<4>(var.second)[i].second; 476 myArray.push_back(segments); 477 } 478 479 returnJson["SegmentFlags"] = myArray; 480 res.jsonValue["Record"] = returnJson; 481 res.end(); 482 return; 483 } 484 } 485 } 486 487 void handleGetLockListAPI(const crow::Request& req, crow::Response& res, 488 const ListOfSessionIds& listSessionIds) 489 { 490 BMCWEB_LOG_DEBUG << listSessionIds.size(); 491 492 auto status = 493 crow::ibm_mc_lock::Lock::getInstance().getLockList(listSessionIds); 494 auto var = std::get<std::vector<std::pair<uint32_t, LockRequests>>>(status); 495 496 nlohmann::json lockRecords = nlohmann::json::array(); 497 498 for (const auto& transactionId : var) 499 { 500 for (const auto& lockRecord : transactionId.second) 501 { 502 nlohmann::json returnJson; 503 504 returnJson["TransactionID"] = transactionId.first; 505 returnJson["SessionID"] = std::get<0>(lockRecord); 506 returnJson["HMCID"] = std::get<1>(lockRecord); 507 returnJson["LockType"] = std::get<2>(lockRecord); 508 returnJson["ResourceID"] = std::get<3>(lockRecord); 509 510 nlohmann::json segments; 511 nlohmann::json segmentInfoArray = nlohmann::json::array(); 512 513 for (const auto& segment : std::get<4>(lockRecord)) 514 { 515 segments["LockFlag"] = segment.first; 516 segments["SegmentLength"] = segment.second; 517 segmentInfoArray.push_back(segments); 518 } 519 520 returnJson["SegmentFlags"] = segmentInfoArray; 521 lockRecords.push_back(returnJson); 522 } 523 } 524 res.result(boost::beast::http::status::ok); 525 res.jsonValue["Records"] = lockRecords; 526 res.end(); 527 } 528 529 template <typename... Middlewares> 530 void requestRoutes(Crow<Middlewares...>& app) 531 { 532 533 // allowed only for admin 534 BMCWEB_ROUTE(app, "/ibm/v1/") 535 .requires({"ConfigureComponents", "ConfigureManager"}) 536 .methods("GET"_method)( 537 [](const crow::Request& req, crow::Response& res) { 538 res.jsonValue["@odata.type"] = 539 "#ibmServiceRoot.v1_0_0.ibmServiceRoot"; 540 res.jsonValue["@odata.id"] = "/ibm/v1/"; 541 res.jsonValue["Id"] = "IBM Rest RootService"; 542 res.jsonValue["Name"] = "IBM Service Root"; 543 res.jsonValue["ConfigFiles"] = { 544 {"@odata.id", "/ibm/v1/Host/ConfigFiles"}}; 545 res.jsonValue["LockService"] = { 546 {"@odata.id", "/ibm/v1/HMC/LockService"}}; 547 res.end(); 548 }); 549 550 BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles") 551 .requires({"ConfigureComponents", "ConfigureManager"}) 552 .methods("GET"_method)( 553 [](const crow::Request& req, crow::Response& res) { 554 handleConfigFileList(res); 555 }); 556 557 BMCWEB_ROUTE(app, 558 "/ibm/v1/Host/ConfigFiles/Actions/FileCollection.DeleteAll") 559 .requires({"ConfigureComponents", "ConfigureManager"}) 560 .methods("POST"_method)( 561 [](const crow::Request& req, crow::Response& res) { 562 deleteConfigFiles(res); 563 }); 564 565 BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles/<path>") 566 .requires({"ConfigureComponents", "ConfigureManager"}) 567 .methods("PUT"_method, "GET"_method, "DELETE"_method)( 568 [](const crow::Request& req, crow::Response& res, 569 const std::string& path) { handleFileUrl(req, res, path); }); 570 571 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService") 572 .requires({"ConfigureComponents", "ConfigureManager"}) 573 .methods("GET"_method)( 574 [](const crow::Request& req, crow::Response& res) { 575 getLockServiceData(res); 576 }); 577 578 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock") 579 .requires({"ConfigureComponents", "ConfigureManager"}) 580 .methods("POST"_method)( 581 [](const crow::Request& req, crow::Response& res) { 582 std::vector<nlohmann::json> body; 583 if (!redfish::json_util::readJson(req, res, "Request", body)) 584 { 585 BMCWEB_LOG_DEBUG << "Not a Valid JSON"; 586 res.result(boost::beast::http::status::bad_request); 587 res.end(); 588 return; 589 } 590 handleAcquireLockAPI(req, res, body); 591 }); 592 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock") 593 .requires({"ConfigureComponents", "ConfigureManager"}) 594 .methods( 595 "POST"_method)([](const crow::Request& req, crow::Response& res) { 596 std::string type; 597 std::vector<uint32_t> listTransactionIds; 598 599 if (!redfish::json_util::readJson(req, res, "Type", type, 600 "TransactionIDs", 601 listTransactionIds)) 602 { 603 res.result(boost::beast::http::status::bad_request); 604 res.end(); 605 return; 606 } 607 if (type == "Transaction") 608 { 609 handleReleaseLockAPI(req, res, listTransactionIds); 610 } 611 else if (type == "Session") 612 { 613 handleRelaseAllAPI(req, res); 614 } 615 else 616 { 617 BMCWEB_LOG_DEBUG << " Value of Type : " << type 618 << "is Not a Valid key"; 619 redfish::messages::propertyValueNotInList(res, type, "Type"); 620 } 621 }); 622 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.GetLockList") 623 .requires({"ConfigureComponents", "ConfigureManager"}) 624 .methods("POST"_method)( 625 [](const crow::Request& req, crow::Response& res) { 626 ListOfSessionIds listSessionIds; 627 628 if (!redfish::json_util::readJson(req, res, "SessionIDs", 629 listSessionIds)) 630 { 631 res.result(boost::beast::http::status::bad_request); 632 res.end(); 633 return; 634 } 635 handleGetLockListAPI(req, res, listSessionIds); 636 }); 637 } 638 639 } // namespace ibm_mc 640 } // namespace crow 641