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