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