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.message()); 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.message()); 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.message()); 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.message()); 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.message()); 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 else 224 { 225 BMCWEB_LOG_DEBUG("config file is created"); 226 asyncResp->res.jsonValue["Description"] = "File Created"; 227 } 228 } 229 230 inline void 231 handleConfigFileList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 232 { 233 std::vector<std::string> pathObjList; 234 std::filesystem::path loc( 235 "/var/lib/bmcweb/ibm-management-console/configfiles"); 236 if (std::filesystem::exists(loc) && std::filesystem::is_directory(loc)) 237 { 238 for (const auto& file : std::filesystem::directory_iterator(loc)) 239 { 240 const std::filesystem::path& pathObj = file.path(); 241 if (std::filesystem::is_regular_file(pathObj)) 242 { 243 pathObjList.emplace_back("/ibm/v1/Host/ConfigFiles/" + 244 pathObj.filename().string()); 245 } 246 } 247 } 248 asyncResp->res.jsonValue["@odata.type"] = 249 "#IBMConfigFile.v1_0_0.IBMConfigFile"; 250 asyncResp->res.jsonValue["@odata.id"] = "/ibm/v1/Host/ConfigFiles/"; 251 asyncResp->res.jsonValue["Id"] = "ConfigFiles"; 252 asyncResp->res.jsonValue["Name"] = "ConfigFiles"; 253 254 asyncResp->res.jsonValue["Members"] = std::move(pathObjList); 255 asyncResp->res.jsonValue["Actions"]["#IBMConfigFiles.DeleteAll"] = { 256 {"target", 257 "/ibm/v1/Host/ConfigFiles/Actions/IBMConfigFiles.DeleteAll"}}; 258 } 259 260 inline void 261 deleteConfigFiles(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 262 { 263 std::error_code ec; 264 std::filesystem::path loc( 265 "/var/lib/bmcweb/ibm-management-console/configfiles"); 266 if (std::filesystem::exists(loc) && std::filesystem::is_directory(loc)) 267 { 268 std::filesystem::remove_all(loc, ec); 269 if (ec) 270 { 271 asyncResp->res.result( 272 boost::beast::http::status::internal_server_error); 273 asyncResp->res.jsonValue["Description"] = internalServerError; 274 BMCWEB_LOG_DEBUG("deleteConfigFiles: Failed to delete the " 275 "config files directory. ec : {}", 276 ec.message()); 277 } 278 } 279 } 280 281 inline void 282 getLockServiceData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 283 { 284 asyncResp->res.jsonValue["@odata.type"] = "#LockService.v1_0_0.LockService"; 285 asyncResp->res.jsonValue["@odata.id"] = "/ibm/v1/HMC/LockService/"; 286 asyncResp->res.jsonValue["Id"] = "LockService"; 287 asyncResp->res.jsonValue["Name"] = "LockService"; 288 289 asyncResp->res.jsonValue["Actions"]["#LockService.AcquireLock"] = { 290 {"target", "/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock"}}; 291 asyncResp->res.jsonValue["Actions"]["#LockService.ReleaseLock"] = { 292 {"target", "/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock"}}; 293 asyncResp->res.jsonValue["Actions"]["#LockService.GetLockList"] = { 294 {"target", "/ibm/v1/HMC/LockService/Actions/LockService.GetLockList"}}; 295 } 296 297 inline void handleFileGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 298 const std::string& fileID) 299 { 300 BMCWEB_LOG_DEBUG("HandleGet on SaveArea files on path: {}", fileID); 301 std::filesystem::path loc( 302 "/var/lib/bmcweb/ibm-management-console/configfiles/" + fileID); 303 if (!std::filesystem::exists(loc) || !std::filesystem::is_regular_file(loc)) 304 { 305 BMCWEB_LOG_WARNING("{} Not found", loc.string()); 306 asyncResp->res.result(boost::beast::http::status::not_found); 307 asyncResp->res.jsonValue["Description"] = resourceNotFoundMsg; 308 return; 309 } 310 311 std::ifstream readfile(loc.string()); 312 if (!readfile) 313 { 314 BMCWEB_LOG_WARNING("{} Not found", loc.string()); 315 asyncResp->res.result(boost::beast::http::status::not_found); 316 asyncResp->res.jsonValue["Description"] = resourceNotFoundMsg; 317 return; 318 } 319 320 std::string contentDispositionParam = "attachment; filename=\"" + fileID + 321 "\""; 322 asyncResp->res.addHeader(boost::beast::http::field::content_disposition, 323 contentDispositionParam); 324 std::string fileData; 325 fileData = {std::istreambuf_iterator<char>(readfile), 326 std::istreambuf_iterator<char>()}; 327 asyncResp->res.jsonValue["Data"] = fileData; 328 } 329 330 inline void 331 handleFileDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 332 const std::string& fileID) 333 { 334 std::string filePath("/var/lib/bmcweb/ibm-management-console/configfiles/" + 335 fileID); 336 BMCWEB_LOG_DEBUG("Removing the file : {}", filePath); 337 std::ifstream fileOpen(filePath.c_str()); 338 if (static_cast<bool>(fileOpen)) 339 { 340 if (remove(filePath.c_str()) == 0) 341 { 342 BMCWEB_LOG_DEBUG("File removed!"); 343 asyncResp->res.jsonValue["Description"] = "File Deleted"; 344 } 345 else 346 { 347 BMCWEB_LOG_ERROR("File not removed!"); 348 asyncResp->res.result( 349 boost::beast::http::status::internal_server_error); 350 asyncResp->res.jsonValue["Description"] = internalServerError; 351 } 352 } 353 else 354 { 355 BMCWEB_LOG_WARNING("File not found!"); 356 asyncResp->res.result(boost::beast::http::status::not_found); 357 asyncResp->res.jsonValue["Description"] = resourceNotFoundMsg; 358 } 359 } 360 361 inline void 362 handleBroadcastService(const crow::Request& req, 363 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 364 { 365 std::string broadcastMsg; 366 367 if (!redfish::json_util::readJsonPatch(req, asyncResp->res, "Message", 368 broadcastMsg)) 369 { 370 BMCWEB_LOG_DEBUG("Not a Valid JSON"); 371 asyncResp->res.result(boost::beast::http::status::bad_request); 372 return; 373 } 374 if (broadcastMsg.size() > maxBroadcastMsgSize) 375 { 376 BMCWEB_LOG_ERROR("Message size exceeds maximum allowed size[1KB]"); 377 asyncResp->res.result(boost::beast::http::status::bad_request); 378 return; 379 } 380 } 381 382 inline void handleFileUrl(const crow::Request& req, 383 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 384 const std::string& fileID) 385 { 386 if (req.method() == boost::beast::http::verb::put) 387 { 388 handleFilePut(req, asyncResp, fileID); 389 return; 390 } 391 if (req.method() == boost::beast::http::verb::get) 392 { 393 handleFileGet(asyncResp, fileID); 394 return; 395 } 396 if (req.method() == boost::beast::http::verb::delete_) 397 { 398 handleFileDelete(asyncResp, fileID); 399 return; 400 } 401 } 402 403 inline void 404 handleAcquireLockAPI(const crow::Request& req, 405 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 406 std::vector<nlohmann::json> body) 407 { 408 LockRequests lockRequestStructure; 409 for (auto& element : body) 410 { 411 std::string lockType; 412 uint64_t resourceId = 0; 413 414 SegmentFlags segInfo; 415 std::vector<nlohmann::json> segmentFlags; 416 417 if (!redfish::json_util::readJson(element, asyncResp->res, "LockType", 418 lockType, "ResourceID", resourceId, 419 "SegmentFlags", segmentFlags)) 420 { 421 BMCWEB_LOG_DEBUG("Not a Valid JSON"); 422 asyncResp->res.result(boost::beast::http::status::bad_request); 423 return; 424 } 425 BMCWEB_LOG_DEBUG("{}", lockType); 426 BMCWEB_LOG_DEBUG("{}", resourceId); 427 428 BMCWEB_LOG_DEBUG("Segment Flags are present"); 429 430 for (auto& e : segmentFlags) 431 { 432 std::string lockFlags; 433 uint32_t segmentLength = 0; 434 435 if (!redfish::json_util::readJson(e, asyncResp->res, "LockFlag", 436 lockFlags, "SegmentLength", 437 segmentLength)) 438 { 439 asyncResp->res.result(boost::beast::http::status::bad_request); 440 return; 441 } 442 443 BMCWEB_LOG_DEBUG("Lockflag : {}", lockFlags); 444 BMCWEB_LOG_DEBUG("SegmentLength : {}", segmentLength); 445 446 segInfo.emplace_back(std::make_pair(lockFlags, segmentLength)); 447 } 448 449 lockRequestStructure.emplace_back(make_tuple( 450 req.session->uniqueId, req.session->clientId.value_or(""), lockType, 451 resourceId, segInfo)); 452 } 453 454 // print lock request into journal 455 456 for (auto& i : lockRequestStructure) 457 { 458 BMCWEB_LOG_DEBUG("{}", std::get<0>(i)); 459 BMCWEB_LOG_DEBUG("{}", std::get<1>(i)); 460 BMCWEB_LOG_DEBUG("{}", std::get<2>(i)); 461 BMCWEB_LOG_DEBUG("{}", std::get<3>(i)); 462 463 for (const auto& p : std::get<4>(i)) 464 { 465 BMCWEB_LOG_DEBUG("{}, {}", p.first, p.second); 466 } 467 } 468 469 const LockRequests& t = lockRequestStructure; 470 471 auto varAcquireLock = crow::ibm_mc_lock::Lock::getInstance().acquireLock(t); 472 473 if (varAcquireLock.first) 474 { 475 // Either validity failure of there is a conflict with itself 476 477 auto validityStatus = 478 std::get<std::pair<bool, int>>(varAcquireLock.second); 479 480 if ((!validityStatus.first) && (validityStatus.second == 0)) 481 { 482 BMCWEB_LOG_DEBUG("Not a Valid record"); 483 BMCWEB_LOG_DEBUG("Bad json in request"); 484 asyncResp->res.result(boost::beast::http::status::bad_request); 485 return; 486 } 487 if (validityStatus.first && (validityStatus.second == 1)) 488 { 489 BMCWEB_LOG_ERROR("There is a conflict within itself"); 490 asyncResp->res.result(boost::beast::http::status::conflict); 491 return; 492 } 493 } 494 else 495 { 496 auto conflictStatus = 497 std::get<crow::ibm_mc_lock::Rc>(varAcquireLock.second); 498 if (!conflictStatus.first) 499 { 500 BMCWEB_LOG_DEBUG("There is no conflict with the locktable"); 501 asyncResp->res.result(boost::beast::http::status::ok); 502 503 auto var = std::get<uint32_t>(conflictStatus.second); 504 nlohmann::json returnJson; 505 returnJson["id"] = var; 506 asyncResp->res.jsonValue["TransactionID"] = var; 507 return; 508 } 509 BMCWEB_LOG_DEBUG("There is a conflict with the lock table"); 510 asyncResp->res.result(boost::beast::http::status::conflict); 511 auto var = 512 std::get<std::pair<uint32_t, LockRequest>>(conflictStatus.second); 513 nlohmann::json returnJson; 514 nlohmann::json segments; 515 nlohmann::json myarray = nlohmann::json::array(); 516 returnJson["TransactionID"] = var.first; 517 returnJson["SessionID"] = std::get<0>(var.second); 518 returnJson["HMCID"] = std::get<1>(var.second); 519 returnJson["LockType"] = std::get<2>(var.second); 520 returnJson["ResourceID"] = std::get<3>(var.second); 521 522 for (const auto& i : std::get<4>(var.second)) 523 { 524 segments["LockFlag"] = i.first; 525 segments["SegmentLength"] = i.second; 526 myarray.push_back(segments); 527 } 528 529 returnJson["SegmentFlags"] = myarray; 530 BMCWEB_LOG_ERROR("Conflicting lock record: {}", returnJson); 531 asyncResp->res.jsonValue["Record"] = returnJson; 532 return; 533 } 534 } 535 inline void 536 handleRelaseAllAPI(const crow::Request& req, 537 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 538 { 539 crow::ibm_mc_lock::Lock::getInstance().releaseLock(req.session->uniqueId); 540 asyncResp->res.result(boost::beast::http::status::ok); 541 } 542 543 inline void 544 handleReleaseLockAPI(const crow::Request& req, 545 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 546 const std::vector<uint32_t>& listTransactionIds) 547 { 548 BMCWEB_LOG_DEBUG("{}", listTransactionIds.size()); 549 BMCWEB_LOG_DEBUG("Data is present"); 550 for (unsigned int listTransactionId : listTransactionIds) 551 { 552 BMCWEB_LOG_DEBUG("{}", listTransactionId); 553 } 554 555 // validate the request ids 556 557 auto varReleaselock = crow::ibm_mc_lock::Lock::getInstance().releaseLock( 558 listTransactionIds, std::make_pair(req.session->clientId.value_or(""), 559 req.session->uniqueId)); 560 561 if (!varReleaselock.first) 562 { 563 // validation Failed 564 BMCWEB_LOG_ERROR("handleReleaseLockAPI: validation failed"); 565 asyncResp->res.result(boost::beast::http::status::bad_request); 566 return; 567 } 568 auto statusRelease = 569 std::get<crow::ibm_mc_lock::RcRelaseLock>(varReleaselock.second); 570 if (statusRelease.first) 571 { 572 // The current hmc owns all the locks, so we already released 573 // them 574 return; 575 } 576 577 // valid rid, but the current hmc does not own all the locks 578 BMCWEB_LOG_DEBUG("Current HMC does not own all the locks"); 579 asyncResp->res.result(boost::beast::http::status::unauthorized); 580 581 auto var = statusRelease.second; 582 nlohmann::json returnJson; 583 nlohmann::json segments; 584 nlohmann::json myArray = nlohmann::json::array(); 585 returnJson["TransactionID"] = var.first; 586 returnJson["SessionID"] = std::get<0>(var.second); 587 returnJson["HMCID"] = std::get<1>(var.second); 588 returnJson["LockType"] = std::get<2>(var.second); 589 returnJson["ResourceID"] = std::get<3>(var.second); 590 591 for (const auto& i : std::get<4>(var.second)) 592 { 593 segments["LockFlag"] = i.first; 594 segments["SegmentLength"] = i.second; 595 myArray.push_back(segments); 596 } 597 598 returnJson["SegmentFlags"] = myArray; 599 BMCWEB_LOG_DEBUG("handleReleaseLockAPI: lockrecord: {}", returnJson); 600 asyncResp->res.jsonValue["Record"] = returnJson; 601 } 602 603 inline void 604 handleGetLockListAPI(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 605 const ListOfSessionIds& listSessionIds) 606 { 607 BMCWEB_LOG_DEBUG("{}", listSessionIds.size()); 608 609 auto status = 610 crow::ibm_mc_lock::Lock::getInstance().getLockList(listSessionIds); 611 auto var = std::get<std::vector<std::pair<uint32_t, LockRequests>>>(status); 612 613 nlohmann::json lockRecords = nlohmann::json::array(); 614 615 for (const auto& transactionId : var) 616 { 617 for (const auto& lockRecord : transactionId.second) 618 { 619 nlohmann::json returnJson; 620 621 returnJson["TransactionID"] = transactionId.first; 622 returnJson["SessionID"] = std::get<0>(lockRecord); 623 returnJson["HMCID"] = std::get<1>(lockRecord); 624 returnJson["LockType"] = std::get<2>(lockRecord); 625 returnJson["ResourceID"] = std::get<3>(lockRecord); 626 627 nlohmann::json segments; 628 nlohmann::json segmentInfoArray = nlohmann::json::array(); 629 630 for (const auto& segment : std::get<4>(lockRecord)) 631 { 632 segments["LockFlag"] = segment.first; 633 segments["SegmentLength"] = segment.second; 634 segmentInfoArray.push_back(segments); 635 } 636 637 returnJson["SegmentFlags"] = segmentInfoArray; 638 lockRecords.push_back(returnJson); 639 } 640 } 641 asyncResp->res.result(boost::beast::http::status::ok); 642 asyncResp->res.jsonValue["Records"] = lockRecords; 643 } 644 645 inline bool isValidConfigFileName(const std::string& fileName, 646 crow::Response& res) 647 { 648 if (fileName.empty()) 649 { 650 BMCWEB_LOG_ERROR("Empty filename"); 651 res.jsonValue["Description"] = "Empty file path in the url"; 652 return false; 653 } 654 655 // ConfigFile name is allowed to take upper and lowercase letters, 656 // numbers and hyphen 657 std::size_t found = fileName.find_first_not_of( 658 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-"); 659 if (found != std::string::npos) 660 { 661 BMCWEB_LOG_ERROR("Unsupported character in filename: {}", fileName); 662 res.jsonValue["Description"] = "Unsupported character in filename"; 663 return false; 664 } 665 666 // Check the filename length 667 if (fileName.length() > 20) 668 { 669 BMCWEB_LOG_ERROR("Name must be maximum 20 characters. " 670 "Input filename length is: {}", 671 fileName.length()); 672 res.jsonValue["Description"] = "Filename must be maximum 20 characters"; 673 return false; 674 } 675 676 return true; 677 } 678 679 inline void requestRoutes(App& app) 680 { 681 // allowed only for admin 682 BMCWEB_ROUTE(app, "/ibm/v1/") 683 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 684 .methods(boost::beast::http::verb::get)( 685 [](const crow::Request&, 686 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 687 asyncResp->res.jsonValue["@odata.type"] = 688 "#ibmServiceRoot.v1_0_0.ibmServiceRoot"; 689 asyncResp->res.jsonValue["@odata.id"] = "/ibm/v1/"; 690 asyncResp->res.jsonValue["Id"] = "IBM Rest RootService"; 691 asyncResp->res.jsonValue["Name"] = "IBM Service Root"; 692 asyncResp->res.jsonValue["ConfigFiles"]["@odata.id"] = 693 "/ibm/v1/Host/ConfigFiles"; 694 asyncResp->res.jsonValue["LockService"]["@odata.id"] = 695 "/ibm/v1/HMC/LockService"; 696 asyncResp->res.jsonValue["BroadcastService"]["@odata.id"] = 697 "/ibm/v1/HMC/BroadcastService"; 698 }); 699 700 BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles") 701 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 702 .methods(boost::beast::http::verb::get)( 703 [](const crow::Request&, 704 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 705 handleConfigFileList(asyncResp); 706 }); 707 708 BMCWEB_ROUTE(app, 709 "/ibm/v1/Host/ConfigFiles/Actions/IBMConfigFiles.DeleteAll") 710 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 711 .methods(boost::beast::http::verb::post)( 712 [](const crow::Request&, 713 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 714 deleteConfigFiles(asyncResp); 715 }); 716 717 BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles/<str>") 718 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 719 .methods(boost::beast::http::verb::put, boost::beast::http::verb::get, 720 boost::beast::http::verb::delete_)( 721 [](const crow::Request& req, 722 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 723 const std::string& fileName) { 724 BMCWEB_LOG_DEBUG("ConfigFile : {}", fileName); 725 // Validate the incoming fileName 726 if (!isValidConfigFileName(fileName, asyncResp->res)) 727 { 728 asyncResp->res.result(boost::beast::http::status::bad_request); 729 return; 730 } 731 handleFileUrl(req, asyncResp, fileName); 732 }); 733 734 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService") 735 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 736 .methods(boost::beast::http::verb::get)( 737 [](const crow::Request&, 738 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 739 getLockServiceData(asyncResp); 740 }); 741 742 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock") 743 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 744 .methods(boost::beast::http::verb::post)( 745 [](const crow::Request& req, 746 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 747 std::vector<nlohmann::json> body; 748 if (!redfish::json_util::readJsonAction(req, asyncResp->res, "Request", 749 body)) 750 { 751 BMCWEB_LOG_DEBUG("Not a Valid JSON"); 752 asyncResp->res.result(boost::beast::http::status::bad_request); 753 return; 754 } 755 handleAcquireLockAPI(req, asyncResp, body); 756 }); 757 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock") 758 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 759 .methods(boost::beast::http::verb::post)( 760 [](const crow::Request& req, 761 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 762 std::string type; 763 std::vector<uint32_t> listTransactionIds; 764 765 if (!redfish::json_util::readJsonPatch(req, asyncResp->res, "Type", 766 type, "TransactionIDs", 767 listTransactionIds)) 768 { 769 asyncResp->res.result(boost::beast::http::status::bad_request); 770 return; 771 } 772 if (type == "Transaction") 773 { 774 handleReleaseLockAPI(req, asyncResp, listTransactionIds); 775 } 776 else if (type == "Session") 777 { 778 handleRelaseAllAPI(req, asyncResp); 779 } 780 else 781 { 782 BMCWEB_LOG_DEBUG(" Value of Type : {}is Not a Valid key", type); 783 redfish::messages::propertyValueNotInList(asyncResp->res, type, 784 "Type"); 785 } 786 }); 787 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.GetLockList") 788 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 789 .methods(boost::beast::http::verb::post)( 790 [](const crow::Request& req, 791 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 792 ListOfSessionIds listSessionIds; 793 794 if (!redfish::json_util::readJsonPatch(req, asyncResp->res, 795 "SessionIDs", listSessionIds)) 796 { 797 asyncResp->res.result(boost::beast::http::status::bad_request); 798 return; 799 } 800 handleGetLockListAPI(asyncResp, listSessionIds); 801 }); 802 803 BMCWEB_ROUTE(app, "/ibm/v1/HMC/BroadcastService") 804 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 805 .methods(boost::beast::http::verb::post)( 806 [](const crow::Request& req, 807 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 808 handleBroadcastService(req, asyncResp); 809 }); 810 } 811 812 } // namespace ibm_mc 813 } // namespace crow 814