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