1 #pragma once 2 #include <tinyxml2.h> 3 4 #include <app.hpp> 5 #include <async_resp.hpp> 6 #include <boost/algorithm/string.hpp> 7 #include <boost/container/flat_set.hpp> 8 #include <error_messages.hpp> 9 #include <event_service_manager.hpp> 10 #include <ibm/locks.hpp> 11 #include <nlohmann/json.hpp> 12 #include <resource_messages.hpp> 13 #include <sdbusplus/message/types.hpp> 14 #include <utils/json_utils.hpp> 15 16 #include <filesystem> 17 #include <fstream> 18 19 using SType = std::string; 20 using SegmentFlags = std::vector<std::pair<std::string, uint32_t>>; 21 using LockRequest = std::tuple<SType, SType, SType, uint64_t, SegmentFlags>; 22 using LockRequests = std::vector<LockRequest>; 23 using Rc = std::pair<bool, std::variant<uint32_t, LockRequest>>; 24 using RcGetLockList = 25 std::variant<std::string, std::vector<std::pair<uint32_t, LockRequests>>>; 26 using ListOfSessionIds = std::vector<std::string>; 27 namespace crow 28 { 29 namespace ibm_mc 30 { 31 constexpr const char* methodNotAllowedMsg = "Method Not Allowed"; 32 constexpr const char* resourceNotFoundMsg = "Resource Not Found"; 33 constexpr const char* contentNotAcceptableMsg = "Content Not Acceptable"; 34 constexpr const char* internalServerError = "Internal Server Error"; 35 36 constexpr size_t maxSaveareaDirSize = 37 10000000; // Allow save area dir size to be max 10MB 38 constexpr size_t minSaveareaFileSize = 39 100; // Allow save area file size of minimum 100B 40 constexpr size_t maxSaveareaFileSize = 41 500000; // Allow save area file size upto 500KB 42 constexpr size_t maxBroadcastMsgSize = 43 1000; // Allow Broadcast message size upto 1KB 44 45 inline 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 (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; 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[10MB]"; 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 pathObjList.push_back("/ibm/v1/Host/ConfigFiles/" + 248 pathObj.filename().string()); 249 } 250 } 251 asyncResp->res.jsonValue["@odata.type"] = 252 "#IBMConfigFile.v1_0_0.IBMConfigFile"; 253 asyncResp->res.jsonValue["@odata.id"] = "/ibm/v1/Host/ConfigFiles/"; 254 asyncResp->res.jsonValue["Id"] = "ConfigFiles"; 255 asyncResp->res.jsonValue["Name"] = "ConfigFiles"; 256 257 asyncResp->res.jsonValue["Members"] = std::move(pathObjList); 258 asyncResp->res.jsonValue["Actions"]["#IBMConfigFiles.DeleteAll"] = { 259 {"target", 260 "/ibm/v1/Host/ConfigFiles/Actions/IBMConfigFiles.DeleteAll"}}; 261 return; 262 } 263 264 inline void 265 deleteConfigFiles(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 266 { 267 std::vector<std::string> pathObjList; 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 return; 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 return; 302 } 303 304 inline void handleFileGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 305 const std::string& fileID) 306 { 307 BMCWEB_LOG_DEBUG << "HandleGet on SaveArea files on path: " << fileID; 308 std::filesystem::path loc( 309 "/var/lib/bmcweb/ibm-management-console/configfiles/" + fileID); 310 if (!std::filesystem::exists(loc)) 311 { 312 BMCWEB_LOG_ERROR << loc << "Not found"; 313 asyncResp->res.result(boost::beast::http::status::not_found); 314 asyncResp->res.jsonValue["Description"] = resourceNotFoundMsg; 315 return; 316 } 317 318 std::ifstream readfile(loc.string()); 319 if (!readfile) 320 { 321 BMCWEB_LOG_ERROR << loc.string() << "Not found"; 322 asyncResp->res.result(boost::beast::http::status::not_found); 323 asyncResp->res.jsonValue["Description"] = resourceNotFoundMsg; 324 return; 325 } 326 327 std::string contentDispositionParam = 328 "attachment; filename=\"" + fileID + "\""; 329 asyncResp->res.addHeader("Content-Disposition", contentDispositionParam); 330 std::string fileData; 331 fileData = {std::istreambuf_iterator<char>(readfile), 332 std::istreambuf_iterator<char>()}; 333 asyncResp->res.jsonValue["Data"] = fileData; 334 return; 335 } 336 337 inline void 338 handleFileDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 339 const std::string& fileID) 340 { 341 std::string filePath("/var/lib/bmcweb/ibm-management-console/configfiles/" + 342 fileID); 343 BMCWEB_LOG_DEBUG << "Removing the file : " << filePath << "\n"; 344 std::ifstream fileOpen(filePath.c_str()); 345 if (static_cast<bool>(fileOpen)) 346 { 347 if (remove(filePath.c_str()) == 0) 348 { 349 BMCWEB_LOG_DEBUG << "File removed!\n"; 350 asyncResp->res.jsonValue["Description"] = "File Deleted"; 351 } 352 else 353 { 354 BMCWEB_LOG_ERROR << "File not removed!\n"; 355 asyncResp->res.result( 356 boost::beast::http::status::internal_server_error); 357 asyncResp->res.jsonValue["Description"] = internalServerError; 358 } 359 } 360 else 361 { 362 BMCWEB_LOG_ERROR << "File not found!\n"; 363 asyncResp->res.result(boost::beast::http::status::not_found); 364 asyncResp->res.jsonValue["Description"] = resourceNotFoundMsg; 365 } 366 return; 367 } 368 369 inline void 370 handleBroadcastService(const crow::Request& req, 371 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 372 { 373 std::string broadcastMsg; 374 375 if (!redfish::json_util::readJson(req, asyncResp->res, "Message", 376 broadcastMsg)) 377 { 378 BMCWEB_LOG_DEBUG << "Not a Valid JSON"; 379 asyncResp->res.result(boost::beast::http::status::bad_request); 380 return; 381 } 382 if (broadcastMsg.size() > maxBroadcastMsgSize) 383 { 384 BMCWEB_LOG_ERROR << "Message size exceeds maximum allowed size[1KB]"; 385 asyncResp->res.result(boost::beast::http::status::bad_request); 386 return; 387 } 388 redfish::EventServiceManager::getInstance().sendBroadcastMsg(broadcastMsg); 389 return; 390 } 391 392 inline void handleFileUrl(const crow::Request& req, 393 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 394 const std::string& fileID) 395 { 396 if (req.method() == boost::beast::http::verb::put) 397 { 398 handleFilePut(req, asyncResp, fileID); 399 return; 400 } 401 if (req.method() == boost::beast::http::verb::get) 402 { 403 handleFileGet(asyncResp, fileID); 404 return; 405 } 406 if (req.method() == boost::beast::http::verb::delete_) 407 { 408 handleFileDelete(asyncResp, fileID); 409 return; 410 } 411 } 412 413 inline void 414 handleAcquireLockAPI(const crow::Request& req, 415 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 416 std::vector<nlohmann::json> body) 417 { 418 LockRequests lockRequestStructure; 419 for (auto& element : body) 420 { 421 std::string lockType; 422 uint64_t resourceId; 423 424 SegmentFlags segInfo; 425 std::vector<nlohmann::json> segmentFlags; 426 427 if (!redfish::json_util::readJson(element, asyncResp->res, "LockType", 428 lockType, "ResourceID", resourceId, 429 "SegmentFlags", segmentFlags)) 430 { 431 BMCWEB_LOG_DEBUG << "Not a Valid JSON"; 432 asyncResp->res.result(boost::beast::http::status::bad_request); 433 return; 434 } 435 BMCWEB_LOG_DEBUG << lockType; 436 BMCWEB_LOG_DEBUG << resourceId; 437 438 BMCWEB_LOG_DEBUG << "Segment Flags are present"; 439 440 for (auto& e : segmentFlags) 441 { 442 std::string lockFlags; 443 uint32_t segmentLength; 444 445 if (!redfish::json_util::readJson(e, asyncResp->res, "LockFlag", 446 lockFlags, "SegmentLength", 447 segmentLength)) 448 { 449 asyncResp->res.result(boost::beast::http::status::bad_request); 450 return; 451 } 452 453 BMCWEB_LOG_DEBUG << "Lockflag : " << lockFlags; 454 BMCWEB_LOG_DEBUG << "SegmentLength : " << segmentLength; 455 456 segInfo.push_back(std::make_pair(lockFlags, segmentLength)); 457 } 458 lockRequestStructure.push_back( 459 make_tuple(req.session->uniqueId, req.session->clientId, lockType, 460 resourceId, segInfo)); 461 } 462 463 // print lock request into journal 464 465 for (auto& i : lockRequestStructure) 466 { 467 BMCWEB_LOG_DEBUG << std::get<0>(i); 468 BMCWEB_LOG_DEBUG << std::get<1>(i); 469 BMCWEB_LOG_DEBUG << std::get<2>(i); 470 BMCWEB_LOG_DEBUG << std::get<3>(i); 471 472 for (const auto& p : std::get<4>(i)) 473 { 474 BMCWEB_LOG_DEBUG << p.first << ", " << p.second; 475 } 476 } 477 478 const LockRequests& t = lockRequestStructure; 479 480 auto varAcquireLock = crow::ibm_mc_lock::Lock::getInstance().acquireLock(t); 481 482 if (varAcquireLock.first) 483 { 484 // Either validity failure of there is a conflict with itself 485 486 auto validityStatus = 487 std::get<std::pair<bool, int>>(varAcquireLock.second); 488 489 if ((!validityStatus.first) && (validityStatus.second == 0)) 490 { 491 BMCWEB_LOG_DEBUG << "Not a Valid record"; 492 BMCWEB_LOG_DEBUG << "Bad json in request"; 493 asyncResp->res.result(boost::beast::http::status::bad_request); 494 return; 495 } 496 if (validityStatus.first && (validityStatus.second == 1)) 497 { 498 BMCWEB_LOG_DEBUG << "There is a conflict within itself"; 499 asyncResp->res.result(boost::beast::http::status::bad_request); 500 return; 501 } 502 } 503 else 504 { 505 auto conflictStatus = 506 std::get<crow::ibm_mc_lock::Rc>(varAcquireLock.second); 507 if (!conflictStatus.first) 508 { 509 BMCWEB_LOG_DEBUG << "There is no conflict with the locktable"; 510 asyncResp->res.result(boost::beast::http::status::ok); 511 512 auto var = std::get<uint32_t>(conflictStatus.second); 513 nlohmann::json returnJson; 514 returnJson["id"] = var; 515 asyncResp->res.jsonValue["TransactionID"] = var; 516 return; 517 } 518 BMCWEB_LOG_DEBUG << "There is a conflict with the lock table"; 519 asyncResp->res.result(boost::beast::http::status::conflict); 520 auto var = 521 std::get<std::pair<uint32_t, LockRequest>>(conflictStatus.second); 522 nlohmann::json returnJson, segments; 523 nlohmann::json myarray = nlohmann::json::array(); 524 returnJson["TransactionID"] = var.first; 525 returnJson["SessionID"] = std::get<0>(var.second); 526 returnJson["HMCID"] = std::get<1>(var.second); 527 returnJson["LockType"] = std::get<2>(var.second); 528 returnJson["ResourceID"] = std::get<3>(var.second); 529 530 for (auto& i : std::get<4>(var.second)) 531 { 532 segments["LockFlag"] = i.first; 533 segments["SegmentLength"] = i.second; 534 myarray.push_back(segments); 535 } 536 537 returnJson["SegmentFlags"] = myarray; 538 539 asyncResp->res.jsonValue["Record"] = returnJson; 540 return; 541 } 542 } 543 inline void 544 handleRelaseAllAPI(const crow::Request& req, 545 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 546 { 547 crow::ibm_mc_lock::Lock::getInstance().releaseLock(req.session->uniqueId); 548 asyncResp->res.result(boost::beast::http::status::ok); 549 return; 550 } 551 552 inline void 553 handleReleaseLockAPI(const crow::Request& req, 554 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 555 const std::vector<uint32_t>& listTransactionIds) 556 { 557 BMCWEB_LOG_DEBUG << listTransactionIds.size(); 558 BMCWEB_LOG_DEBUG << "Data is present"; 559 for (unsigned int listTransactionId : listTransactionIds) 560 { 561 BMCWEB_LOG_DEBUG << listTransactionId; 562 } 563 564 // validate the request ids 565 566 auto varReleaselock = crow::ibm_mc_lock::Lock::getInstance().releaseLock( 567 listTransactionIds, 568 std::make_pair(req.session->clientId, req.session->uniqueId)); 569 570 if (!varReleaselock.first) 571 { 572 // validation Failed 573 asyncResp->res.result(boost::beast::http::status::bad_request); 574 return; 575 } 576 auto statusRelease = 577 std::get<crow::ibm_mc_lock::RcRelaseLock>(varReleaselock.second); 578 if (statusRelease.first) 579 { 580 // The current hmc owns all the locks, so we already released 581 // them 582 return; 583 } 584 585 // valid rid, but the current hmc does not own all the locks 586 BMCWEB_LOG_DEBUG << "Current HMC does not own all the locks"; 587 asyncResp->res.result(boost::beast::http::status::unauthorized); 588 589 auto var = statusRelease.second; 590 nlohmann::json returnJson, 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 (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 asyncResp->res.jsonValue["Record"] = returnJson; 607 return; 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 689 // allowed only for admin 690 BMCWEB_ROUTE(app, "/ibm/v1/") 691 .privileges({"ConfigureComponents", "ConfigureManager"}) 692 .methods(boost::beast::http::verb::get)( 693 [](const crow::Request&, 694 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 695 asyncResp->res.jsonValue["@odata.type"] = 696 "#ibmServiceRoot.v1_0_0.ibmServiceRoot"; 697 asyncResp->res.jsonValue["@odata.id"] = "/ibm/v1/"; 698 asyncResp->res.jsonValue["Id"] = "IBM Rest RootService"; 699 asyncResp->res.jsonValue["Name"] = "IBM Service Root"; 700 asyncResp->res.jsonValue["ConfigFiles"] = { 701 {"@odata.id", "/ibm/v1/Host/ConfigFiles"}}; 702 asyncResp->res.jsonValue["LockService"] = { 703 {"@odata.id", "/ibm/v1/HMC/LockService"}}; 704 asyncResp->res.jsonValue["BroadcastService"] = { 705 {"@odata.id", "/ibm/v1/HMC/BroadcastService"}}; 706 }); 707 708 BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles") 709 .privileges({"ConfigureComponents", "ConfigureManager"}) 710 .methods(boost::beast::http::verb::get)( 711 [](const crow::Request&, 712 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 713 handleConfigFileList(asyncResp); 714 }); 715 716 BMCWEB_ROUTE(app, 717 "/ibm/v1/Host/ConfigFiles/Actions/IBMConfigFiles.DeleteAll") 718 .privileges({"ConfigureComponents", "ConfigureManager"}) 719 .methods(boost::beast::http::verb::post)( 720 [](const crow::Request&, 721 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 722 deleteConfigFiles(asyncResp); 723 }); 724 725 BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles/<str>") 726 .privileges({"ConfigureComponents", "ConfigureManager"}) 727 .methods(boost::beast::http::verb::put, boost::beast::http::verb::get, 728 boost::beast::http::verb::delete_)( 729 [](const crow::Request& req, 730 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 731 const std::string& fileName) { 732 BMCWEB_LOG_DEBUG << "ConfigFile : " << fileName; 733 // Validate the incoming fileName 734 if (!isValidConfigFileName(fileName, asyncResp->res)) 735 { 736 asyncResp->res.result( 737 boost::beast::http::status::bad_request); 738 return; 739 } 740 handleFileUrl(req, asyncResp, fileName); 741 }); 742 743 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService") 744 .privileges({"ConfigureComponents", "ConfigureManager"}) 745 .methods(boost::beast::http::verb::get)( 746 [](const crow::Request&, 747 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 748 getLockServiceData(asyncResp); 749 }); 750 751 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock") 752 .privileges({"ConfigureComponents", "ConfigureManager"}) 753 .methods(boost::beast::http::verb::post)( 754 [](const crow::Request& req, 755 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 756 std::vector<nlohmann::json> body; 757 if (!redfish::json_util::readJson(req, asyncResp->res, 758 "Request", body)) 759 { 760 BMCWEB_LOG_DEBUG << "Not a Valid JSON"; 761 asyncResp->res.result( 762 boost::beast::http::status::bad_request); 763 return; 764 } 765 handleAcquireLockAPI(req, asyncResp, body); 766 }); 767 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock") 768 .privileges({"ConfigureComponents", "ConfigureManager"}) 769 .methods(boost::beast::http::verb::post)( 770 [](const crow::Request& req, 771 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 772 std::string type; 773 std::vector<uint32_t> listTransactionIds; 774 775 if (!redfish::json_util::readJson(req, asyncResp->res, "Type", 776 type, "TransactionIDs", 777 listTransactionIds)) 778 { 779 asyncResp->res.result( 780 boost::beast::http::status::bad_request); 781 return; 782 } 783 if (type == "Transaction") 784 { 785 handleReleaseLockAPI(req, asyncResp, listTransactionIds); 786 } 787 else if (type == "Session") 788 { 789 handleRelaseAllAPI(req, asyncResp); 790 } 791 else 792 { 793 BMCWEB_LOG_DEBUG << " Value of Type : " << type 794 << "is Not a Valid key"; 795 redfish::messages::propertyValueNotInList(asyncResp->res, 796 type, "Type"); 797 } 798 }); 799 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.GetLockList") 800 .privileges({"ConfigureComponents", "ConfigureManager"}) 801 .methods(boost::beast::http::verb::post)( 802 [](const crow::Request& req, 803 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 804 ListOfSessionIds listSessionIds; 805 806 if (!redfish::json_util::readJson(req, asyncResp->res, 807 "SessionIDs", listSessionIds)) 808 { 809 asyncResp->res.result( 810 boost::beast::http::status::bad_request); 811 return; 812 } 813 handleGetLockListAPI(asyncResp, listSessionIds); 814 }); 815 816 BMCWEB_ROUTE(app, "/ibm/v1/HMC/BroadcastService") 817 .privileges({"ConfigureComponents", "ConfigureManager"}) 818 .methods(boost::beast::http::verb::post)( 819 [](const crow::Request& req, 820 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 821 handleBroadcastService(req, asyncResp); 822 }); 823 } 824 825 } // namespace ibm_mc 826 } // namespace crow 827