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