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() == boost::beast::http::verb::put) 257 { 258 handleFilePut(req, res, fileID); 259 res.end(); 260 return; 261 } 262 if (req.method() == boost::beast::http::verb::get) 263 { 264 handleFileGet(res, fileID); 265 res.end(); 266 return; 267 } 268 if (req.method() == boost::beast::http::verb::delete_) 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( 321 make_tuple(req.session->uniqueId, req.session->clientId, lockType, 322 resourceId, segInfo)); 323 } 324 325 // print lock request into journal 326 327 for (uint32_t i = 0; i < lockRequestStructure.size(); i++) 328 { 329 BMCWEB_LOG_DEBUG << std::get<0>(lockRequestStructure[i]); 330 BMCWEB_LOG_DEBUG << std::get<1>(lockRequestStructure[i]); 331 BMCWEB_LOG_DEBUG << std::get<2>(lockRequestStructure[i]); 332 BMCWEB_LOG_DEBUG << std::get<3>(lockRequestStructure[i]); 333 334 for (const auto& p : std::get<4>(lockRequestStructure[i])) 335 { 336 BMCWEB_LOG_DEBUG << p.first << ", " << p.second; 337 } 338 } 339 340 const LockRequests& t = lockRequestStructure; 341 342 auto varAcquireLock = crow::ibm_mc_lock::Lock::getInstance().acquireLock(t); 343 344 if (varAcquireLock.first) 345 { 346 // Either validity failure of there is a conflict with itself 347 348 auto validityStatus = 349 std::get<std::pair<bool, int>>(varAcquireLock.second); 350 351 if ((!validityStatus.first) && (validityStatus.second == 0)) 352 { 353 BMCWEB_LOG_DEBUG << "Not a Valid record"; 354 BMCWEB_LOG_DEBUG << "Bad json in request"; 355 res.result(boost::beast::http::status::bad_request); 356 res.end(); 357 return; 358 } 359 if (validityStatus.first && (validityStatus.second == 1)) 360 { 361 BMCWEB_LOG_DEBUG << "There is a conflict within itself"; 362 res.result(boost::beast::http::status::bad_request); 363 res.end(); 364 return; 365 } 366 } 367 else 368 { 369 auto conflictStatus = 370 std::get<crow::ibm_mc_lock::Rc>(varAcquireLock.second); 371 if (!conflictStatus.first) 372 { 373 BMCWEB_LOG_DEBUG << "There is no conflict with the locktable"; 374 res.result(boost::beast::http::status::ok); 375 376 auto var = std::get<uint32_t>(conflictStatus.second); 377 nlohmann::json returnJson; 378 returnJson["id"] = var; 379 res.jsonValue["TransactionID"] = var; 380 res.end(); 381 return; 382 } 383 else 384 { 385 BMCWEB_LOG_DEBUG << "There is a conflict with the lock table"; 386 res.result(boost::beast::http::status::conflict); 387 auto var = std::get<std::pair<uint32_t, LockRequest>>( 388 conflictStatus.second); 389 nlohmann::json returnJson, segments; 390 nlohmann::json myarray = nlohmann::json::array(); 391 returnJson["TransactionID"] = var.first; 392 returnJson["SessionID"] = std::get<0>(var.second); 393 returnJson["HMCID"] = std::get<1>(var.second); 394 returnJson["LockType"] = std::get<2>(var.second); 395 returnJson["ResourceID"] = std::get<3>(var.second); 396 397 for (uint32_t i = 0; i < std::get<4>(var.second).size(); i++) 398 { 399 segments["LockFlag"] = std::get<4>(var.second)[i].first; 400 segments["SegmentLength"] = std::get<4>(var.second)[i].second; 401 myarray.push_back(segments); 402 } 403 404 returnJson["SegmentFlags"] = myarray; 405 406 res.jsonValue["Record"] = returnJson; 407 res.end(); 408 return; 409 } 410 } 411 } 412 void handleRelaseAllAPI(const crow::Request& req, crow::Response& res) 413 { 414 crow::ibm_mc_lock::Lock::getInstance().releaseLock(req.session->uniqueId); 415 res.result(boost::beast::http::status::ok); 416 res.end(); 417 return; 418 } 419 420 void handleReleaseLockAPI(const crow::Request& req, crow::Response& res, 421 const std::vector<uint32_t>& listTransactionIds) 422 { 423 BMCWEB_LOG_DEBUG << listTransactionIds.size(); 424 BMCWEB_LOG_DEBUG << "Data is present"; 425 for (uint32_t i = 0; i < listTransactionIds.size(); i++) 426 { 427 BMCWEB_LOG_DEBUG << listTransactionIds[i]; 428 } 429 430 // validate the request ids 431 432 auto varReleaselock = crow::ibm_mc_lock::Lock::getInstance().releaseLock( 433 listTransactionIds, 434 std::make_pair(req.session->clientId, req.session->uniqueId)); 435 436 if (!varReleaselock.first) 437 { 438 // validation Failed 439 res.result(boost::beast::http::status::bad_request); 440 res.end(); 441 return; 442 } 443 else 444 { 445 auto statusRelease = 446 std::get<crow::ibm_mc_lock::RcRelaseLock>(varReleaselock.second); 447 if (statusRelease.first) 448 { 449 // The current hmc owns all the locks, so we already released 450 // them 451 res.result(boost::beast::http::status::ok); 452 res.end(); 453 return; 454 } 455 456 else 457 { 458 // valid rid, but the current hmc does not own all the locks 459 BMCWEB_LOG_DEBUG << "Current HMC does not own all the locks"; 460 res.result(boost::beast::http::status::unauthorized); 461 462 auto var = statusRelease.second; 463 nlohmann::json returnJson, segments; 464 nlohmann::json myArray = nlohmann::json::array(); 465 returnJson["TransactionID"] = var.first; 466 returnJson["SessionID"] = std::get<0>(var.second); 467 returnJson["HMCID"] = std::get<1>(var.second); 468 returnJson["LockType"] = std::get<2>(var.second); 469 returnJson["ResourceID"] = std::get<3>(var.second); 470 471 for (uint32_t i = 0; i < std::get<4>(var.second).size(); i++) 472 { 473 segments["LockFlag"] = std::get<4>(var.second)[i].first; 474 segments["SegmentLength"] = std::get<4>(var.second)[i].second; 475 myArray.push_back(segments); 476 } 477 478 returnJson["SegmentFlags"] = myArray; 479 res.jsonValue["Record"] = returnJson; 480 res.end(); 481 return; 482 } 483 } 484 } 485 486 void handleGetLockListAPI(const crow::Request& req, crow::Response& res, 487 const ListOfSessionIds& listSessionIds) 488 { 489 BMCWEB_LOG_DEBUG << listSessionIds.size(); 490 491 auto status = 492 crow::ibm_mc_lock::Lock::getInstance().getLockList(listSessionIds); 493 auto var = std::get<std::vector<std::pair<uint32_t, LockRequests>>>(status); 494 495 nlohmann::json lockRecords = nlohmann::json::array(); 496 497 for (const auto& transactionId : var) 498 { 499 for (const auto& lockRecord : transactionId.second) 500 { 501 nlohmann::json returnJson; 502 503 returnJson["TransactionID"] = transactionId.first; 504 returnJson["SessionID"] = std::get<0>(lockRecord); 505 returnJson["HMCID"] = std::get<1>(lockRecord); 506 returnJson["LockType"] = std::get<2>(lockRecord); 507 returnJson["ResourceID"] = std::get<3>(lockRecord); 508 509 nlohmann::json segments; 510 nlohmann::json segmentInfoArray = nlohmann::json::array(); 511 512 for (const auto& segment : std::get<4>(lockRecord)) 513 { 514 segments["LockFlag"] = segment.first; 515 segments["SegmentLength"] = segment.second; 516 segmentInfoArray.push_back(segments); 517 } 518 519 returnJson["SegmentFlags"] = segmentInfoArray; 520 lockRecords.push_back(returnJson); 521 } 522 } 523 res.result(boost::beast::http::status::ok); 524 res.jsonValue["Records"] = lockRecords; 525 res.end(); 526 } 527 528 template <typename... Middlewares> 529 void requestRoutes(Crow<Middlewares...>& app) 530 { 531 532 // allowed only for admin 533 BMCWEB_ROUTE(app, "/ibm/v1/") 534 .requires({"ConfigureComponents", "ConfigureManager"}) 535 .methods(boost::beast::http::verb::get)( 536 [](const crow::Request& req, crow::Response& res) { 537 res.jsonValue["@odata.type"] = 538 "#ibmServiceRoot.v1_0_0.ibmServiceRoot"; 539 res.jsonValue["@odata.id"] = "/ibm/v1/"; 540 res.jsonValue["Id"] = "IBM Rest RootService"; 541 res.jsonValue["Name"] = "IBM Service Root"; 542 res.jsonValue["ConfigFiles"] = { 543 {"@odata.id", "/ibm/v1/Host/ConfigFiles"}}; 544 res.jsonValue["LockService"] = { 545 {"@odata.id", "/ibm/v1/HMC/LockService"}}; 546 res.end(); 547 }); 548 549 BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles") 550 .requires({"ConfigureComponents", "ConfigureManager"}) 551 .methods(boost::beast::http::verb::get)( 552 [](const crow::Request& req, crow::Response& res) { 553 handleConfigFileList(res); 554 }); 555 556 BMCWEB_ROUTE(app, 557 "/ibm/v1/Host/ConfigFiles/Actions/FileCollection.DeleteAll") 558 .requires({"ConfigureComponents", "ConfigureManager"}) 559 .methods(boost::beast::http::verb::post)( 560 [](const crow::Request& req, crow::Response& res) { 561 deleteConfigFiles(res); 562 }); 563 564 BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles/<path>") 565 .requires({"ConfigureComponents", "ConfigureManager"}) 566 .methods(boost::beast::http::verb::put, boost::beast::http::verb::get, 567 boost::beast::http::verb::delete_)( 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(boost::beast::http::verb::get)( 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(boost::beast::http::verb::post)( 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(boost::beast::http::verb::post)([](const crow::Request& req, 595 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(boost::beast::http::verb::post)( 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