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