1 #pragma once 2 #include <tinyxml2.h> 3 4 #include <app.hpp> 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 <event_service_manager.hpp> 10 #include <ibm/locks.hpp> 11 #include <nlohmann/json.hpp> 12 #include <resource_messages.hpp> 13 #include <sdbusplus/message/types.hpp> 14 #include <utils/json_utils.hpp> 15 16 #include <filesystem> 17 #include <fstream> 18 19 using SType = std::string; 20 using SegmentFlags = std::vector<std::pair<std::string, uint32_t>>; 21 using LockRequest = std::tuple<SType, SType, SType, uint64_t, SegmentFlags>; 22 using LockRequests = std::vector<LockRequest>; 23 using Rc = std::pair<bool, std::variant<uint32_t, LockRequest>>; 24 using RcGetLockList = 25 std::variant<std::string, std::vector<std::pair<uint32_t, LockRequests>>>; 26 using ListOfSessionIds = std::vector<std::string>; 27 namespace crow 28 { 29 namespace ibm_mc 30 { 31 constexpr const char* methodNotAllowedMsg = "Method Not Allowed"; 32 constexpr const char* resourceNotFoundMsg = "Resource Not Found"; 33 constexpr const char* contentNotAcceptableMsg = "Content Not Acceptable"; 34 constexpr const char* internalServerError = "Internal Server Error"; 35 36 constexpr size_t maxSaveareaDirSize = 37 10000000; // Allow save area dir size to be max 10MB 38 constexpr size_t minSaveareaFileSize = 39 100; // Allow save area file size of minimum 100B 40 constexpr size_t maxSaveareaFileSize = 41 500000; // Allow save area file size upto 500KB 42 constexpr size_t maxBroadcastMsgSize = 43 1000; // Allow Broadcast message size upto 1KB 44 45 inline bool 46 createSaveAreaPath(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 47 { 48 // The path /var/lib/obmc will be created by initrdscripts 49 // Create the directories for the save-area files, when we get 50 // first file upload request 51 std::error_code ec; 52 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt", ec)) 53 { 54 std::filesystem::create_directory("/var/lib/obmc/bmc-console-mgmt", ec); 55 } 56 if (ec) 57 { 58 asyncResp->res.result( 59 boost::beast::http::status::internal_server_error); 60 asyncResp->res.jsonValue["Description"] = internalServerError; 61 BMCWEB_LOG_DEBUG 62 << "handleIbmPost: Failed to prepare save-area directory. ec : " 63 << ec; 64 return false; 65 } 66 67 if (!std::filesystem::is_directory( 68 "/var/lib/obmc/bmc-console-mgmt/save-area", ec)) 69 { 70 std::filesystem::create_directory( 71 "/var/lib/obmc/bmc-console-mgmt/save-area", ec); 72 } 73 if (ec) 74 { 75 asyncResp->res.result( 76 boost::beast::http::status::internal_server_error); 77 asyncResp->res.jsonValue["Description"] = internalServerError; 78 BMCWEB_LOG_DEBUG 79 << "handleIbmPost: Failed to prepare save-area directory. ec : " 80 << ec; 81 return false; 82 } 83 return true; 84 } 85 86 inline void handleFilePut(const crow::Request& req, 87 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 88 const std::string& fileID) 89 { 90 std::error_code ec; 91 // Check the content-type of the request 92 boost::beast::string_view contentType = req.getHeaderValue("content-type"); 93 if (!boost::iequals(contentType, "application/octet-stream")) 94 { 95 asyncResp->res.result(boost::beast::http::status::not_acceptable); 96 asyncResp->res.jsonValue["Description"] = contentNotAcceptableMsg; 97 return; 98 } 99 BMCWEB_LOG_DEBUG 100 << "File upload in application/octet-stream format. Continue.."; 101 102 BMCWEB_LOG_DEBUG 103 << "handleIbmPut: Request to create/update the save-area file"; 104 if (!createSaveAreaPath(asyncResp)) 105 { 106 asyncResp->res.result(boost::beast::http::status::not_found); 107 asyncResp->res.jsonValue["Description"] = resourceNotFoundMsg; 108 return; 109 } 110 111 std::ofstream file; 112 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area"); 113 114 // Get the current size of the savearea directory 115 std::filesystem::recursive_directory_iterator iter(loc, ec); 116 if (ec) 117 { 118 asyncResp->res.result( 119 boost::beast::http::status::internal_server_error); 120 asyncResp->res.jsonValue["Description"] = internalServerError; 121 BMCWEB_LOG_DEBUG << "handleIbmPut: Failed to prepare save-area " 122 "directory iterator. ec : " 123 << ec; 124 return; 125 } 126 std::uintmax_t saveAreaDirSize = 0; 127 for (auto& it : iter) 128 { 129 if (!std::filesystem::is_directory(it, ec)) 130 { 131 if (ec) 132 { 133 asyncResp->res.result( 134 boost::beast::http::status::internal_server_error); 135 asyncResp->res.jsonValue["Description"] = internalServerError; 136 BMCWEB_LOG_DEBUG << "handleIbmPut: Failed to find save-area " 137 "directory . ec : " 138 << ec; 139 return; 140 } 141 std::uintmax_t fileSize = std::filesystem::file_size(it, ec); 142 if (ec) 143 { 144 asyncResp->res.result( 145 boost::beast::http::status::internal_server_error); 146 asyncResp->res.jsonValue["Description"] = internalServerError; 147 BMCWEB_LOG_DEBUG << "handleIbmPut: Failed to find save-area " 148 "file size inside the directory . ec : " 149 << ec; 150 return; 151 } 152 saveAreaDirSize += fileSize; 153 } 154 } 155 BMCWEB_LOG_DEBUG << "saveAreaDirSize: " << saveAreaDirSize; 156 157 // Get the file size getting uploaded 158 const std::string& data = req.body; 159 BMCWEB_LOG_DEBUG << "data length: " << data.length(); 160 161 if (data.length() < minSaveareaFileSize) 162 { 163 asyncResp->res.result(boost::beast::http::status::bad_request); 164 asyncResp->res.jsonValue["Description"] = 165 "File size is less than minimum allowed size[100B]"; 166 return; 167 } 168 if (data.length() > maxSaveareaFileSize) 169 { 170 asyncResp->res.result(boost::beast::http::status::bad_request); 171 asyncResp->res.jsonValue["Description"] = 172 "File size exceeds maximum allowed size[500KB]"; 173 return; 174 } 175 176 // Form the file path 177 loc /= fileID; 178 BMCWEB_LOG_DEBUG << "Writing to the file: " << loc; 179 180 // Check if the same file exists in the directory 181 bool fileExists = std::filesystem::exists(loc, ec); 182 if (ec) 183 { 184 asyncResp->res.result( 185 boost::beast::http::status::internal_server_error); 186 asyncResp->res.jsonValue["Description"] = internalServerError; 187 BMCWEB_LOG_DEBUG << "handleIbmPut: Failed to find if file exists. ec : " 188 << ec; 189 return; 190 } 191 192 std::uintmax_t newSizeToWrite = 0; 193 if (fileExists) 194 { 195 // File exists. Get the current file size 196 std::uintmax_t currentFileSize = std::filesystem::file_size(loc, ec); 197 if (ec) 198 { 199 asyncResp->res.result( 200 boost::beast::http::status::internal_server_error); 201 asyncResp->res.jsonValue["Description"] = internalServerError; 202 BMCWEB_LOG_DEBUG << "handleIbmPut: Failed to find file size. ec : " 203 << ec; 204 return; 205 } 206 // Calculate the difference in the file size. 207 // If the data.length is greater than the existing file size, then 208 // calculate the difference. Else consider the delta size as zero - 209 // because there is no increase in the total directory size. 210 // We need to add the diff only if the incoming data is larger than the 211 // existing filesize 212 if (data.length() > currentFileSize) 213 { 214 newSizeToWrite = data.length() - currentFileSize; 215 } 216 BMCWEB_LOG_DEBUG << "newSizeToWrite: " << newSizeToWrite; 217 } 218 else 219 { 220 // This is a new file upload 221 newSizeToWrite = data.length(); 222 } 223 224 // Calculate the total dir size before writing the new file 225 BMCWEB_LOG_DEBUG << "total new size: " << saveAreaDirSize + newSizeToWrite; 226 227 if ((saveAreaDirSize + newSizeToWrite) > maxSaveareaDirSize) 228 { 229 asyncResp->res.result(boost::beast::http::status::bad_request); 230 asyncResp->res.jsonValue["Description"] = 231 "File size does not fit in the savearea " 232 "directory maximum allowed size[10MB]"; 233 return; 234 } 235 236 file.open(loc, std::ofstream::out); 237 if (file.fail()) 238 { 239 BMCWEB_LOG_DEBUG << "Error while opening the file for writing"; 240 asyncResp->res.result( 241 boost::beast::http::status::internal_server_error); 242 asyncResp->res.jsonValue["Description"] = 243 "Error while creating the file"; 244 return; 245 } 246 file << data; 247 std::string origin = "/ibm/v1/Host/ConfigFiles/" + fileID; 248 // Push an event 249 if (fileExists) 250 { 251 BMCWEB_LOG_DEBUG << "config file is updated"; 252 asyncResp->res.jsonValue["Description"] = "File Updated"; 253 254 redfish::EventServiceManager::getInstance().sendEvent( 255 redfish::messages::resourceChanged(), origin, "IBMConfigFile"); 256 } 257 else 258 { 259 BMCWEB_LOG_DEBUG << "config file is created"; 260 asyncResp->res.jsonValue["Description"] = "File Created"; 261 262 redfish::EventServiceManager::getInstance().sendEvent( 263 redfish::messages::resourceCreated(), origin, "IBMConfigFile"); 264 } 265 } 266 267 inline void 268 handleConfigFileList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 269 { 270 std::vector<std::string> pathObjList; 271 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area"); 272 if (std::filesystem::exists(loc) && std::filesystem::is_directory(loc)) 273 { 274 for (const auto& file : std::filesystem::directory_iterator(loc)) 275 { 276 const std::filesystem::path& pathObj = file.path(); 277 pathObjList.push_back("/ibm/v1/Host/ConfigFiles/" + 278 pathObj.filename().string()); 279 } 280 } 281 asyncResp->res.jsonValue["@odata.type"] = 282 "#IBMConfigFile.v1_0_0.IBMConfigFile"; 283 asyncResp->res.jsonValue["@odata.id"] = "/ibm/v1/Host/ConfigFiles/"; 284 asyncResp->res.jsonValue["Id"] = "ConfigFiles"; 285 asyncResp->res.jsonValue["Name"] = "ConfigFiles"; 286 287 asyncResp->res.jsonValue["Members"] = std::move(pathObjList); 288 asyncResp->res.jsonValue["Actions"]["#IBMConfigFiles.DeleteAll"] = { 289 {"target", 290 "/ibm/v1/Host/ConfigFiles/Actions/IBMConfigFiles.DeleteAll"}}; 291 return; 292 } 293 294 inline void 295 deleteConfigFiles(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 296 { 297 std::vector<std::string> pathObjList; 298 std::error_code ec; 299 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area"); 300 if (std::filesystem::exists(loc) && std::filesystem::is_directory(loc)) 301 { 302 std::filesystem::remove_all(loc, ec); 303 if (ec) 304 { 305 asyncResp->res.result( 306 boost::beast::http::status::internal_server_error); 307 asyncResp->res.jsonValue["Description"] = internalServerError; 308 BMCWEB_LOG_DEBUG << "deleteConfigFiles: Failed to delete the " 309 "config files directory. ec : " 310 << ec; 311 } 312 } 313 return; 314 } 315 316 inline void 317 getLockServiceData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 318 { 319 asyncResp->res.jsonValue["@odata.type"] = "#LockService.v1_0_0.LockService"; 320 asyncResp->res.jsonValue["@odata.id"] = "/ibm/v1/HMC/LockService/"; 321 asyncResp->res.jsonValue["Id"] = "LockService"; 322 asyncResp->res.jsonValue["Name"] = "LockService"; 323 324 asyncResp->res.jsonValue["Actions"]["#LockService.AcquireLock"] = { 325 {"target", "/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock"}}; 326 asyncResp->res.jsonValue["Actions"]["#LockService.ReleaseLock"] = { 327 {"target", "/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock"}}; 328 asyncResp->res.jsonValue["Actions"]["#LockService.GetLockList"] = { 329 {"target", "/ibm/v1/HMC/LockService/Actions/LockService.GetLockList"}}; 330 return; 331 } 332 333 inline void handleFileGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 334 const std::string& fileID) 335 { 336 BMCWEB_LOG_DEBUG << "HandleGet on SaveArea files on path: " << fileID; 337 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area/" + 338 fileID); 339 if (!std::filesystem::exists(loc)) 340 { 341 BMCWEB_LOG_ERROR << loc << "Not found"; 342 asyncResp->res.result(boost::beast::http::status::not_found); 343 asyncResp->res.jsonValue["Description"] = resourceNotFoundMsg; 344 return; 345 } 346 347 std::ifstream readfile(loc.string()); 348 if (!readfile) 349 { 350 BMCWEB_LOG_ERROR << loc.string() << "Not found"; 351 asyncResp->res.result(boost::beast::http::status::not_found); 352 asyncResp->res.jsonValue["Description"] = resourceNotFoundMsg; 353 return; 354 } 355 356 std::string contentDispositionParam = 357 "attachment; filename=\"" + fileID + "\""; 358 asyncResp->res.addHeader("Content-Disposition", contentDispositionParam); 359 std::string fileData; 360 fileData = {std::istreambuf_iterator<char>(readfile), 361 std::istreambuf_iterator<char>()}; 362 asyncResp->res.jsonValue["Data"] = fileData; 363 return; 364 } 365 366 inline void 367 handleFileDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 368 const std::string& fileID) 369 { 370 std::string filePath("/var/lib/obmc/bmc-console-mgmt/save-area/" + fileID); 371 BMCWEB_LOG_DEBUG << "Removing the file : " << filePath << "\n"; 372 std::ifstream fileOpen(filePath.c_str()); 373 if (static_cast<bool>(fileOpen)) 374 { 375 if (remove(filePath.c_str()) == 0) 376 { 377 BMCWEB_LOG_DEBUG << "File removed!\n"; 378 asyncResp->res.jsonValue["Description"] = "File Deleted"; 379 } 380 else 381 { 382 BMCWEB_LOG_ERROR << "File not removed!\n"; 383 asyncResp->res.result( 384 boost::beast::http::status::internal_server_error); 385 asyncResp->res.jsonValue["Description"] = internalServerError; 386 } 387 } 388 else 389 { 390 BMCWEB_LOG_ERROR << "File not found!\n"; 391 asyncResp->res.result(boost::beast::http::status::not_found); 392 asyncResp->res.jsonValue["Description"] = resourceNotFoundMsg; 393 } 394 return; 395 } 396 397 inline void 398 handleBroadcastService(const crow::Request& req, 399 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 400 { 401 std::string broadcastMsg; 402 403 if (!redfish::json_util::readJson(req, asyncResp->res, "Message", 404 broadcastMsg)) 405 { 406 BMCWEB_LOG_DEBUG << "Not a Valid JSON"; 407 asyncResp->res.result(boost::beast::http::status::bad_request); 408 return; 409 } 410 if (broadcastMsg.size() > maxBroadcastMsgSize) 411 { 412 BMCWEB_LOG_ERROR << "Message size exceeds maximum allowed size[1KB]"; 413 asyncResp->res.result(boost::beast::http::status::bad_request); 414 return; 415 } 416 redfish::EventServiceManager::getInstance().sendBroadcastMsg(broadcastMsg); 417 return; 418 } 419 420 inline void handleFileUrl(const crow::Request& req, 421 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 422 const std::string& fileID) 423 { 424 if (req.method() == boost::beast::http::verb::put) 425 { 426 handleFilePut(req, asyncResp, fileID); 427 return; 428 } 429 if (req.method() == boost::beast::http::verb::get) 430 { 431 handleFileGet(asyncResp, fileID); 432 return; 433 } 434 if (req.method() == boost::beast::http::verb::delete_) 435 { 436 handleFileDelete(asyncResp, fileID); 437 return; 438 } 439 } 440 441 inline void 442 handleAcquireLockAPI(const crow::Request& req, 443 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 444 std::vector<nlohmann::json> body) 445 { 446 LockRequests lockRequestStructure; 447 for (auto& element : body) 448 { 449 std::string lockType; 450 uint64_t resourceId; 451 452 SegmentFlags segInfo; 453 std::vector<nlohmann::json> segmentFlags; 454 455 if (!redfish::json_util::readJson(element, asyncResp->res, "LockType", 456 lockType, "ResourceID", resourceId, 457 "SegmentFlags", segmentFlags)) 458 { 459 BMCWEB_LOG_DEBUG << "Not a Valid JSON"; 460 asyncResp->res.result(boost::beast::http::status::bad_request); 461 return; 462 } 463 BMCWEB_LOG_DEBUG << lockType; 464 BMCWEB_LOG_DEBUG << resourceId; 465 466 BMCWEB_LOG_DEBUG << "Segment Flags are present"; 467 468 for (auto& e : segmentFlags) 469 { 470 std::string lockFlags; 471 uint32_t segmentLength; 472 473 if (!redfish::json_util::readJson(e, asyncResp->res, "LockFlag", 474 lockFlags, "SegmentLength", 475 segmentLength)) 476 { 477 asyncResp->res.result(boost::beast::http::status::bad_request); 478 return; 479 } 480 481 BMCWEB_LOG_DEBUG << "Lockflag : " << lockFlags; 482 BMCWEB_LOG_DEBUG << "SegmentLength : " << segmentLength; 483 484 segInfo.push_back(std::make_pair(lockFlags, segmentLength)); 485 } 486 lockRequestStructure.push_back( 487 make_tuple(req.session->uniqueId, req.session->clientId, lockType, 488 resourceId, segInfo)); 489 } 490 491 // print lock request into journal 492 493 for (auto& i : lockRequestStructure) 494 { 495 BMCWEB_LOG_DEBUG << std::get<0>(i); 496 BMCWEB_LOG_DEBUG << std::get<1>(i); 497 BMCWEB_LOG_DEBUG << std::get<2>(i); 498 BMCWEB_LOG_DEBUG << std::get<3>(i); 499 500 for (const auto& p : std::get<4>(i)) 501 { 502 BMCWEB_LOG_DEBUG << p.first << ", " << p.second; 503 } 504 } 505 506 const LockRequests& t = lockRequestStructure; 507 508 auto varAcquireLock = crow::ibm_mc_lock::Lock::getInstance().acquireLock(t); 509 510 if (varAcquireLock.first) 511 { 512 // Either validity failure of there is a conflict with itself 513 514 auto validityStatus = 515 std::get<std::pair<bool, int>>(varAcquireLock.second); 516 517 if ((!validityStatus.first) && (validityStatus.second == 0)) 518 { 519 BMCWEB_LOG_DEBUG << "Not a Valid record"; 520 BMCWEB_LOG_DEBUG << "Bad json in request"; 521 asyncResp->res.result(boost::beast::http::status::bad_request); 522 return; 523 } 524 if (validityStatus.first && (validityStatus.second == 1)) 525 { 526 BMCWEB_LOG_DEBUG << "There is a conflict within itself"; 527 asyncResp->res.result(boost::beast::http::status::bad_request); 528 return; 529 } 530 } 531 else 532 { 533 auto conflictStatus = 534 std::get<crow::ibm_mc_lock::Rc>(varAcquireLock.second); 535 if (!conflictStatus.first) 536 { 537 BMCWEB_LOG_DEBUG << "There is no conflict with the locktable"; 538 asyncResp->res.result(boost::beast::http::status::ok); 539 540 auto var = std::get<uint32_t>(conflictStatus.second); 541 nlohmann::json returnJson; 542 returnJson["id"] = var; 543 asyncResp->res.jsonValue["TransactionID"] = var; 544 return; 545 } 546 BMCWEB_LOG_DEBUG << "There is a conflict with the lock table"; 547 asyncResp->res.result(boost::beast::http::status::conflict); 548 auto var = 549 std::get<std::pair<uint32_t, LockRequest>>(conflictStatus.second); 550 nlohmann::json returnJson, segments; 551 nlohmann::json myarray = nlohmann::json::array(); 552 returnJson["TransactionID"] = var.first; 553 returnJson["SessionID"] = std::get<0>(var.second); 554 returnJson["HMCID"] = std::get<1>(var.second); 555 returnJson["LockType"] = std::get<2>(var.second); 556 returnJson["ResourceID"] = std::get<3>(var.second); 557 558 for (auto& i : std::get<4>(var.second)) 559 { 560 segments["LockFlag"] = i.first; 561 segments["SegmentLength"] = i.second; 562 myarray.push_back(segments); 563 } 564 565 returnJson["SegmentFlags"] = myarray; 566 567 asyncResp->res.jsonValue["Record"] = returnJson; 568 return; 569 } 570 } 571 inline void 572 handleRelaseAllAPI(const crow::Request& req, 573 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 574 { 575 crow::ibm_mc_lock::Lock::getInstance().releaseLock(req.session->uniqueId); 576 asyncResp->res.result(boost::beast::http::status::ok); 577 return; 578 } 579 580 inline void 581 handleReleaseLockAPI(const crow::Request& req, 582 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 583 const std::vector<uint32_t>& listTransactionIds) 584 { 585 BMCWEB_LOG_DEBUG << listTransactionIds.size(); 586 BMCWEB_LOG_DEBUG << "Data is present"; 587 for (unsigned int listTransactionId : listTransactionIds) 588 { 589 BMCWEB_LOG_DEBUG << listTransactionId; 590 } 591 592 // validate the request ids 593 594 auto varReleaselock = crow::ibm_mc_lock::Lock::getInstance().releaseLock( 595 listTransactionIds, 596 std::make_pair(req.session->clientId, req.session->uniqueId)); 597 598 if (!varReleaselock.first) 599 { 600 // validation Failed 601 asyncResp->res.result(boost::beast::http::status::bad_request); 602 return; 603 } 604 auto statusRelease = 605 std::get<crow::ibm_mc_lock::RcRelaseLock>(varReleaselock.second); 606 if (statusRelease.first) 607 { 608 // The current hmc owns all the locks, so we already released 609 // them 610 return; 611 } 612 613 // valid rid, but the current hmc does not own all the locks 614 BMCWEB_LOG_DEBUG << "Current HMC does not own all the locks"; 615 asyncResp->res.result(boost::beast::http::status::unauthorized); 616 617 auto var = statusRelease.second; 618 nlohmann::json returnJson, segments; 619 nlohmann::json myArray = nlohmann::json::array(); 620 returnJson["TransactionID"] = var.first; 621 returnJson["SessionID"] = std::get<0>(var.second); 622 returnJson["HMCID"] = std::get<1>(var.second); 623 returnJson["LockType"] = std::get<2>(var.second); 624 returnJson["ResourceID"] = std::get<3>(var.second); 625 626 for (auto& i : std::get<4>(var.second)) 627 { 628 segments["LockFlag"] = i.first; 629 segments["SegmentLength"] = i.second; 630 myArray.push_back(segments); 631 } 632 633 returnJson["SegmentFlags"] = myArray; 634 asyncResp->res.jsonValue["Record"] = returnJson; 635 return; 636 } 637 638 inline void 639 handleGetLockListAPI(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 640 const ListOfSessionIds& listSessionIds) 641 { 642 BMCWEB_LOG_DEBUG << listSessionIds.size(); 643 644 auto status = 645 crow::ibm_mc_lock::Lock::getInstance().getLockList(listSessionIds); 646 auto var = std::get<std::vector<std::pair<uint32_t, LockRequests>>>(status); 647 648 nlohmann::json lockRecords = nlohmann::json::array(); 649 650 for (const auto& transactionId : var) 651 { 652 for (const auto& lockRecord : transactionId.second) 653 { 654 nlohmann::json returnJson; 655 656 returnJson["TransactionID"] = transactionId.first; 657 returnJson["SessionID"] = std::get<0>(lockRecord); 658 returnJson["HMCID"] = std::get<1>(lockRecord); 659 returnJson["LockType"] = std::get<2>(lockRecord); 660 returnJson["ResourceID"] = std::get<3>(lockRecord); 661 662 nlohmann::json segments; 663 nlohmann::json segmentInfoArray = nlohmann::json::array(); 664 665 for (const auto& segment : std::get<4>(lockRecord)) 666 { 667 segments["LockFlag"] = segment.first; 668 segments["SegmentLength"] = segment.second; 669 segmentInfoArray.push_back(segments); 670 } 671 672 returnJson["SegmentFlags"] = segmentInfoArray; 673 lockRecords.push_back(returnJson); 674 } 675 } 676 asyncResp->res.result(boost::beast::http::status::ok); 677 asyncResp->res.jsonValue["Records"] = lockRecords; 678 } 679 680 inline bool isValidConfigFileName(const std::string& fileName, 681 crow::Response& res) 682 { 683 if (fileName.empty()) 684 { 685 BMCWEB_LOG_ERROR << "Empty filename"; 686 res.jsonValue["Description"] = "Empty file path in the url"; 687 return false; 688 } 689 690 // ConfigFile name is allowed to take upper and lowercase letters, 691 // numbers and hyphen 692 std::size_t found = fileName.find_first_not_of( 693 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-"); 694 if (found != std::string::npos) 695 { 696 BMCWEB_LOG_ERROR << "Unsupported character in filename: " << fileName; 697 res.jsonValue["Description"] = "Unsupported character in filename"; 698 return false; 699 } 700 701 // Check the filename length 702 if (fileName.length() > 20) 703 { 704 BMCWEB_LOG_ERROR << "Name must be maximum 20 characters. " 705 "Input filename length is: " 706 << fileName.length(); 707 res.jsonValue["Description"] = "Filename must be maximum 20 characters"; 708 return false; 709 } 710 711 return true; 712 } 713 714 inline void requestRoutes(App& app) 715 { 716 717 // allowed only for admin 718 BMCWEB_ROUTE(app, "/ibm/v1/") 719 .privileges({"ConfigureComponents", "ConfigureManager"}) 720 .methods(boost::beast::http::verb::get)( 721 [](const crow::Request&, 722 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 723 asyncResp->res.jsonValue["@odata.type"] = 724 "#ibmServiceRoot.v1_0_0.ibmServiceRoot"; 725 asyncResp->res.jsonValue["@odata.id"] = "/ibm/v1/"; 726 asyncResp->res.jsonValue["Id"] = "IBM Rest RootService"; 727 asyncResp->res.jsonValue["Name"] = "IBM Service Root"; 728 asyncResp->res.jsonValue["ConfigFiles"] = { 729 {"@odata.id", "/ibm/v1/Host/ConfigFiles"}}; 730 asyncResp->res.jsonValue["LockService"] = { 731 {"@odata.id", "/ibm/v1/HMC/LockService"}}; 732 asyncResp->res.jsonValue["BroadcastService"] = { 733 {"@odata.id", "/ibm/v1/HMC/BroadcastService"}}; 734 }); 735 736 BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles") 737 .privileges({"ConfigureComponents", "ConfigureManager"}) 738 .methods(boost::beast::http::verb::get)( 739 [](const crow::Request&, 740 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 741 handleConfigFileList(asyncResp); 742 }); 743 744 BMCWEB_ROUTE(app, 745 "/ibm/v1/Host/ConfigFiles/Actions/IBMConfigFiles.DeleteAll") 746 .privileges({"ConfigureComponents", "ConfigureManager"}) 747 .methods(boost::beast::http::verb::post)( 748 [](const crow::Request&, 749 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 750 deleteConfigFiles(asyncResp); 751 }); 752 753 BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles/<str>") 754 .privileges({"ConfigureComponents", "ConfigureManager"}) 755 .methods(boost::beast::http::verb::put, boost::beast::http::verb::get, 756 boost::beast::http::verb::delete_)( 757 [](const crow::Request& req, 758 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 759 const std::string& fileName) { 760 BMCWEB_LOG_DEBUG << "ConfigFile : " << fileName; 761 // Validate the incoming fileName 762 if (!isValidConfigFileName(fileName, asyncResp->res)) 763 { 764 asyncResp->res.result( 765 boost::beast::http::status::bad_request); 766 return; 767 } 768 handleFileUrl(req, asyncResp, fileName); 769 }); 770 771 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService") 772 .privileges({"ConfigureComponents", "ConfigureManager"}) 773 .methods(boost::beast::http::verb::get)( 774 [](const crow::Request&, 775 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 776 getLockServiceData(asyncResp); 777 }); 778 779 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock") 780 .privileges({"ConfigureComponents", "ConfigureManager"}) 781 .methods(boost::beast::http::verb::post)( 782 [](const crow::Request& req, 783 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 784 std::vector<nlohmann::json> body; 785 if (!redfish::json_util::readJson(req, asyncResp->res, 786 "Request", body)) 787 { 788 BMCWEB_LOG_DEBUG << "Not a Valid JSON"; 789 asyncResp->res.result( 790 boost::beast::http::status::bad_request); 791 return; 792 } 793 handleAcquireLockAPI(req, asyncResp, body); 794 }); 795 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock") 796 .privileges({"ConfigureComponents", "ConfigureManager"}) 797 .methods(boost::beast::http::verb::post)( 798 [](const crow::Request& req, 799 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 800 std::string type; 801 std::vector<uint32_t> listTransactionIds; 802 803 if (!redfish::json_util::readJson(req, asyncResp->res, "Type", 804 type, "TransactionIDs", 805 listTransactionIds)) 806 { 807 asyncResp->res.result( 808 boost::beast::http::status::bad_request); 809 return; 810 } 811 if (type == "Transaction") 812 { 813 handleReleaseLockAPI(req, asyncResp, listTransactionIds); 814 } 815 else if (type == "Session") 816 { 817 handleRelaseAllAPI(req, asyncResp); 818 } 819 else 820 { 821 BMCWEB_LOG_DEBUG << " Value of Type : " << type 822 << "is Not a Valid key"; 823 redfish::messages::propertyValueNotInList(asyncResp->res, 824 type, "Type"); 825 } 826 }); 827 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.GetLockList") 828 .privileges({"ConfigureComponents", "ConfigureManager"}) 829 .methods(boost::beast::http::verb::post)( 830 [](const crow::Request& req, 831 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 832 ListOfSessionIds listSessionIds; 833 834 if (!redfish::json_util::readJson(req, asyncResp->res, 835 "SessionIDs", listSessionIds)) 836 { 837 asyncResp->res.result( 838 boost::beast::http::status::bad_request); 839 return; 840 } 841 handleGetLockListAPI(asyncResp, listSessionIds); 842 }); 843 844 BMCWEB_ROUTE(app, "/ibm/v1/HMC/BroadcastService") 845 .privileges({"ConfigureComponents", "ConfigureManager"}) 846 .methods(boost::beast::http::verb::post)( 847 [](const crow::Request& req, 848 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 849 handleBroadcastService(req, asyncResp); 850 }); 851 } 852 853 } // namespace ibm_mc 854 } // namespace crow 855