1 #pragma once 2 #include <app.h> 3 #include <tinyxml2.h> 4 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 <filesystem> 10 #include <fstream> 11 #include <ibm/locks.hpp> 12 #include <nlohmann/json.hpp> 13 #include <regex> 14 #include <sdbusplus/message/types.hpp> 15 #include <utils/json_utils.hpp> 16 17 // Allow save area file size to 500KB 18 #define MAX_SAVE_AREA_FILESIZE 500000 19 20 using SType = std::string; 21 using SegmentFlags = std::vector<std::pair<std::string, uint32_t>>; 22 using LockRequest = std::tuple<SType, SType, SType, uint64_t, SegmentFlags>; 23 using LockRequests = std::vector<LockRequest>; 24 using Rc = std::pair<bool, std::variant<uint32_t, LockRequest>>; 25 using RcGetLockList = 26 std::variant<std::string, std::vector<std::pair<uint32_t, LockRequests>>>; 27 using ListOfSessionIds = std::vector<std::string>; 28 namespace crow 29 { 30 namespace ibm_mc 31 { 32 constexpr const char *methodNotAllowedMsg = "Method Not Allowed"; 33 constexpr const char *resourceNotFoundMsg = "Resource Not Found"; 34 constexpr const char *contentNotAcceptableMsg = "Content Not Acceptable"; 35 constexpr const char *internalServerError = "Internal Server Error"; 36 37 bool createSaveAreaPath(crow::Response &res) 38 { 39 // The path /var/lib/obmc will be created by initrdscripts 40 // Create the directories for the save-area files, when we get 41 // first file upload request 42 std::error_code ec; 43 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt", ec)) 44 { 45 std::filesystem::create_directory("/var/lib/obmc/bmc-console-mgmt", ec); 46 } 47 if (ec) 48 { 49 res.result(boost::beast::http::status::internal_server_error); 50 res.jsonValue["Description"] = internalServerError; 51 BMCWEB_LOG_DEBUG 52 << "handleIbmPost: Failed to prepare save-area directory. ec : " 53 << ec; 54 return false; 55 } 56 57 if (!std::filesystem::is_directory( 58 "/var/lib/obmc/bmc-console-mgmt/save-area", ec)) 59 { 60 std::filesystem::create_directory( 61 "/var/lib/obmc/bmc-console-mgmt/save-area", ec); 62 } 63 if (ec) 64 { 65 res.result(boost::beast::http::status::internal_server_error); 66 res.jsonValue["Description"] = internalServerError; 67 BMCWEB_LOG_DEBUG 68 << "handleIbmPost: Failed to prepare save-area directory. ec : " 69 << ec; 70 return false; 71 } 72 return true; 73 } 74 void handleFilePut(const crow::Request &req, crow::Response &res, 75 const std::string &fileID) 76 { 77 // Check the content-type of the request 78 std::string_view contentType = req.getHeaderValue("content-type"); 79 if (boost::starts_with(contentType, "multipart/form-data")) 80 { 81 BMCWEB_LOG_DEBUG 82 << "This is multipart/form-data. Invalid content for PUT"; 83 84 res.result(boost::beast::http::status::not_acceptable); 85 res.jsonValue["Description"] = contentNotAcceptableMsg; 86 return; 87 } 88 else 89 { 90 BMCWEB_LOG_DEBUG << "Not a multipart/form-data. Continue.."; 91 } 92 93 BMCWEB_LOG_DEBUG 94 << "handleIbmPut: Request to create/update the save-area file"; 95 if (!createSaveAreaPath(res)) 96 { 97 res.result(boost::beast::http::status::not_found); 98 res.jsonValue["Description"] = resourceNotFoundMsg; 99 return; 100 } 101 // Create the file 102 std::ofstream file; 103 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area"); 104 loc /= fileID; 105 106 std::string data = std::move(req.body); 107 BMCWEB_LOG_DEBUG << "data capaticty : " << data.capacity(); 108 if (data.capacity() > MAX_SAVE_AREA_FILESIZE) 109 { 110 res.result(boost::beast::http::status::bad_request); 111 res.jsonValue["Description"] = 112 "File size exceeds maximum allowed size[500KB]"; 113 return; 114 } 115 BMCWEB_LOG_DEBUG << "Creating file " << loc; 116 file.open(loc, std::ofstream::out); 117 if (file.fail()) 118 { 119 BMCWEB_LOG_DEBUG << "Error while opening the file for writing"; 120 res.result(boost::beast::http::status::internal_server_error); 121 res.jsonValue["Description"] = "Error while creating the file"; 122 return; 123 } 124 else 125 { 126 file << data; 127 BMCWEB_LOG_DEBUG << "save-area file is created"; 128 res.jsonValue["Description"] = "File Created"; 129 } 130 } 131 132 void handleConfigFileList(crow::Response &res) 133 { 134 std::vector<std::string> pathObjList; 135 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area"); 136 if (std::filesystem::exists(loc) && std::filesystem::is_directory(loc)) 137 { 138 for (const auto &file : std::filesystem::directory_iterator(loc)) 139 { 140 std::filesystem::path pathObj(file.path()); 141 pathObjList.push_back("/ibm/v1/Host/ConfigFiles/" + 142 pathObj.filename().string()); 143 } 144 } 145 res.jsonValue["@odata.type"] = "#FileCollection.v1_0_0.FileCollection"; 146 res.jsonValue["@odata.id"] = "/ibm/v1/Host/ConfigFiles/"; 147 res.jsonValue["Id"] = "ConfigFiles"; 148 res.jsonValue["Name"] = "ConfigFiles"; 149 150 res.jsonValue["Members"] = std::move(pathObjList); 151 res.jsonValue["Actions"]["#FileCollection.DeleteAll"] = { 152 {"target", 153 "/ibm/v1/Host/ConfigFiles/Actions/FileCollection.DeleteAll"}}; 154 res.end(); 155 } 156 157 void deleteConfigFiles(crow::Response &res) 158 { 159 std::vector<std::string> pathObjList; 160 std::error_code ec; 161 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area"); 162 if (std::filesystem::exists(loc) && std::filesystem::is_directory(loc)) 163 { 164 std::filesystem::remove_all(loc, ec); 165 if (ec) 166 { 167 res.result(boost::beast::http::status::internal_server_error); 168 res.jsonValue["Description"] = internalServerError; 169 BMCWEB_LOG_DEBUG << "deleteConfigFiles: Failed to delete the " 170 "config files directory. ec : " 171 << ec; 172 } 173 } 174 res.end(); 175 } 176 177 void getLockServiceData(crow::Response &res) 178 { 179 res.jsonValue["@odata.type"] = "#LockService.v1_0_0.LockService"; 180 res.jsonValue["@odata.id"] = "/ibm/v1/HMC/LockService/"; 181 res.jsonValue["Id"] = "LockService"; 182 res.jsonValue["Name"] = "LockService"; 183 184 res.jsonValue["Actions"]["#LockService.AcquireLock"] = { 185 {"target", "/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock"}}; 186 res.jsonValue["Actions"]["#LockService.ReleaseLock"] = { 187 {"target", "/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock"}}; 188 res.jsonValue["Actions"]["#LockService.GetLockList"] = { 189 {"target", "/ibm/v1/HMC/LockService/Actions/LockService.GetLockList"}}; 190 res.end(); 191 } 192 193 void handleFileGet(crow::Response &res, const std::string &fileID) 194 { 195 BMCWEB_LOG_DEBUG << "HandleGet on SaveArea files on path: " << fileID; 196 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area/" + 197 fileID); 198 if (!std::filesystem::exists(loc)) 199 { 200 BMCWEB_LOG_ERROR << loc << "Not found"; 201 res.result(boost::beast::http::status::not_found); 202 res.jsonValue["Description"] = resourceNotFoundMsg; 203 return; 204 } 205 206 std::ifstream readfile(loc.string()); 207 if (!readfile) 208 { 209 BMCWEB_LOG_ERROR << loc.string() << "Not found"; 210 res.result(boost::beast::http::status::not_found); 211 res.jsonValue["Description"] = resourceNotFoundMsg; 212 return; 213 } 214 215 std::string contentDispositionParam = 216 "attachment; filename=\"" + fileID + "\""; 217 res.addHeader("Content-Disposition", contentDispositionParam); 218 std::string fileData; 219 fileData = {std::istreambuf_iterator<char>(readfile), 220 std::istreambuf_iterator<char>()}; 221 res.jsonValue["Data"] = fileData; 222 return; 223 } 224 225 void handleFileDelete(crow::Response &res, const std::string &fileID) 226 { 227 std::string filePath("/var/lib/obmc/bmc-console-mgmt/save-area/" + fileID); 228 BMCWEB_LOG_DEBUG << "Removing the file : " << filePath << "\n"; 229 230 std::ifstream file_open(filePath.c_str()); 231 if (static_cast<bool>(file_open)) 232 if (remove(filePath.c_str()) == 0) 233 { 234 BMCWEB_LOG_DEBUG << "File removed!\n"; 235 res.jsonValue["Description"] = "File Deleted"; 236 } 237 else 238 { 239 BMCWEB_LOG_ERROR << "File not removed!\n"; 240 res.result(boost::beast::http::status::internal_server_error); 241 res.jsonValue["Description"] = internalServerError; 242 } 243 else 244 { 245 BMCWEB_LOG_ERROR << "File not found!\n"; 246 res.result(boost::beast::http::status::not_found); 247 res.jsonValue["Description"] = resourceNotFoundMsg; 248 } 249 return; 250 } 251 252 inline void handleFileUrl(const crow::Request &req, crow::Response &res, 253 const std::string &fileID) 254 { 255 if (req.method() == "PUT"_method) 256 { 257 handleFilePut(req, res, fileID); 258 res.end(); 259 return; 260 } 261 if (req.method() == "GET"_method) 262 { 263 handleFileGet(res, fileID); 264 res.end(); 265 return; 266 } 267 if (req.method() == "DELETE"_method) 268 { 269 handleFileDelete(res, fileID); 270 res.end(); 271 return; 272 } 273 } 274 275 void handleAcquireLockAPI(const crow::Request &req, crow::Response &res, 276 std::vector<nlohmann::json> body) 277 { 278 LockRequests lockRequestStructure; 279 for (auto &element : body) 280 { 281 std::string lockType; 282 uint64_t resourceId; 283 284 SegmentFlags segInfo; 285 std::vector<nlohmann::json> segmentFlags; 286 287 if (!redfish::json_util::readJson(element, res, "LockType", lockType, 288 "ResourceID", resourceId, 289 "SegmentFlags", segmentFlags)) 290 { 291 BMCWEB_LOG_DEBUG << "Not a Valid JSON"; 292 res.result(boost::beast::http::status::bad_request); 293 res.end(); 294 return; 295 } 296 BMCWEB_LOG_DEBUG << lockType; 297 BMCWEB_LOG_DEBUG << resourceId; 298 299 BMCWEB_LOG_DEBUG << "Segment Flags are present"; 300 301 for (auto &e : segmentFlags) 302 { 303 std::string lockFlags; 304 uint32_t segmentLength; 305 306 if (!redfish::json_util::readJson(e, res, "LockFlag", lockFlags, 307 "SegmentLength", segmentLength)) 308 { 309 res.result(boost::beast::http::status::bad_request); 310 res.end(); 311 return; 312 } 313 314 BMCWEB_LOG_DEBUG << "Lockflag : " << lockFlags; 315 BMCWEB_LOG_DEBUG << "SegmentLength : " << segmentLength; 316 317 segInfo.push_back(std::make_pair(lockFlags, segmentLength)); 318 } 319 lockRequestStructure.push_back(make_tuple( 320 req.session->uniqueId, "hmc-id", lockType, resourceId, segInfo)); 321 } 322 323 // print lock request into journal 324 325 for (uint32_t i = 0; i < lockRequestStructure.size(); i++) 326 { 327 BMCWEB_LOG_DEBUG << std::get<0>(lockRequestStructure[i]); 328 BMCWEB_LOG_DEBUG << std::get<1>(lockRequestStructure[i]); 329 BMCWEB_LOG_DEBUG << std::get<2>(lockRequestStructure[i]); 330 BMCWEB_LOG_DEBUG << std::get<3>(lockRequestStructure[i]); 331 332 for (const auto &p : std::get<4>(lockRequestStructure[i])) 333 { 334 BMCWEB_LOG_DEBUG << p.first << ", " << p.second; 335 } 336 } 337 338 const LockRequests &t = lockRequestStructure; 339 340 auto varAcquireLock = crow::ibm_mc_lock::Lock::getInstance().acquireLock(t); 341 342 if (varAcquireLock.first) 343 { 344 // Either validity failure of there is a conflict with itself 345 346 auto validityStatus = 347 std::get<std::pair<bool, int>>(varAcquireLock.second); 348 349 if ((!validityStatus.first) && (validityStatus.second == 0)) 350 { 351 BMCWEB_LOG_DEBUG << "Not a Valid record"; 352 BMCWEB_LOG_DEBUG << "Bad json in request"; 353 res.result(boost::beast::http::status::bad_request); 354 res.end(); 355 return; 356 } 357 if (validityStatus.first && (validityStatus.second == 1)) 358 { 359 BMCWEB_LOG_DEBUG << "There is a conflict within itself"; 360 res.result(boost::beast::http::status::bad_request); 361 res.end(); 362 return; 363 } 364 } 365 else 366 { 367 auto conflictStatus = 368 std::get<crow::ibm_mc_lock::Rc>(varAcquireLock.second); 369 if (!conflictStatus.first) 370 { 371 BMCWEB_LOG_DEBUG << "There is no conflict with the locktable"; 372 res.result(boost::beast::http::status::ok); 373 374 auto var = std::get<uint32_t>(conflictStatus.second); 375 nlohmann::json returnJson; 376 returnJson["id"] = var; 377 res.jsonValue["TransactionID"] = var; 378 res.end(); 379 return; 380 } 381 else 382 { 383 BMCWEB_LOG_DEBUG << "There is a conflict with the lock table"; 384 res.result(boost::beast::http::status::conflict); 385 auto var = std::get<std::pair<uint32_t, LockRequest>>( 386 conflictStatus.second); 387 nlohmann::json returnJson, segments; 388 nlohmann::json myarray = nlohmann::json::array(); 389 returnJson["TransactionID"] = var.first; 390 returnJson["SessionID"] = std::get<0>(var.second); 391 returnJson["HMCID"] = std::get<1>(var.second); 392 returnJson["LockType"] = std::get<2>(var.second); 393 returnJson["ResourceID"] = std::get<3>(var.second); 394 395 for (uint32_t i = 0; i < std::get<4>(var.second).size(); i++) 396 { 397 segments["LockFlag"] = std::get<4>(var.second)[i].first; 398 segments["SegmentLength"] = std::get<4>(var.second)[i].second; 399 myarray.push_back(segments); 400 } 401 402 returnJson["SegmentFlags"] = myarray; 403 404 res.jsonValue["Record"] = returnJson; 405 res.end(); 406 return; 407 } 408 } 409 } 410 void handleRelaseAllAPI(const crow::Request &req, crow::Response &res) 411 { 412 crow::ibm_mc_lock::Lock::getInstance().releaseLock(req.session->uniqueId); 413 res.result(boost::beast::http::status::ok); 414 res.end(); 415 return; 416 } 417 418 void handleReleaseLockAPI(const crow::Request &req, crow::Response &res, 419 const std::vector<uint32_t> &listTransactionIds) 420 { 421 BMCWEB_LOG_DEBUG << listTransactionIds.size(); 422 BMCWEB_LOG_DEBUG << "Data is present"; 423 for (uint32_t i = 0; i < listTransactionIds.size(); i++) 424 { 425 BMCWEB_LOG_DEBUG << listTransactionIds[i]; 426 } 427 428 std::string clientId = "hmc-id"; 429 std::string sessionId = req.session->uniqueId; 430 431 // validate the request ids 432 433 auto varReleaselock = crow::ibm_mc_lock::Lock::getInstance().releaseLock( 434 listTransactionIds, std::make_pair(clientId, sessionId)); 435 436 if (!varReleaselock.first) 437 { 438 // validation Failed 439 res.result(boost::beast::http::status::bad_request); 440 res.end(); 441 return; 442 } 443 else 444 { 445 auto statusRelease = 446 std::get<crow::ibm_mc_lock::RcRelaseLock>(varReleaselock.second); 447 if (statusRelease.first) 448 { 449 // The current hmc owns all the locks, so we already released 450 // them 451 res.result(boost::beast::http::status::ok); 452 res.end(); 453 return; 454 } 455 456 else 457 { 458 // valid rid, but the current hmc does not own all the locks 459 BMCWEB_LOG_DEBUG << "Current HMC does not own all the locks"; 460 res.result(boost::beast::http::status::unauthorized); 461 462 auto var = statusRelease.second; 463 nlohmann::json returnJson, segments; 464 nlohmann::json myArray = nlohmann::json::array(); 465 returnJson["TransactionID"] = var.first; 466 returnJson["SessionID"] = std::get<0>(var.second); 467 returnJson["HMCID"] = std::get<1>(var.second); 468 returnJson["LockType"] = std::get<2>(var.second); 469 returnJson["ResourceID"] = std::get<3>(var.second); 470 471 for (uint32_t i = 0; i < std::get<4>(var.second).size(); i++) 472 { 473 segments["LockFlag"] = std::get<4>(var.second)[i].first; 474 segments["SegmentLength"] = std::get<4>(var.second)[i].second; 475 myArray.push_back(segments); 476 } 477 478 returnJson["SegmentFlags"] = myArray; 479 res.jsonValue["Record"] = returnJson; 480 res.end(); 481 return; 482 } 483 } 484 } 485 486 void handleGetLockListAPI(const crow::Request &req, crow::Response &res, 487 const ListOfSessionIds &listSessionIds) 488 { 489 BMCWEB_LOG_DEBUG << listSessionIds.size(); 490 491 auto status = 492 crow::ibm_mc_lock::Lock::getInstance().getLockList(listSessionIds); 493 auto var = std::get<std::vector<std::pair<uint32_t, LockRequests>>>(status); 494 495 nlohmann::json lockRecords = nlohmann::json::array(); 496 497 for (const auto &transactionId : var) 498 { 499 for (const auto &lockRecord : transactionId.second) 500 { 501 nlohmann::json returnJson; 502 503 returnJson["TransactionID"] = transactionId.first; 504 returnJson["SessionID"] = std::get<0>(lockRecord); 505 returnJson["HMCID"] = std::get<1>(lockRecord); 506 returnJson["LockType"] = std::get<2>(lockRecord); 507 returnJson["ResourceID"] = std::get<3>(lockRecord); 508 509 nlohmann::json segments; 510 nlohmann::json segmentInfoArray = nlohmann::json::array(); 511 512 for (const auto &segment : std::get<4>(lockRecord)) 513 { 514 segments["LockFlag"] = segment.first; 515 segments["SegmentLength"] = segment.second; 516 segmentInfoArray.push_back(segments); 517 } 518 519 returnJson["SegmentFlags"] = segmentInfoArray; 520 lockRecords.push_back(returnJson); 521 } 522 } 523 res.result(boost::beast::http::status::ok); 524 res.jsonValue["Records"] = lockRecords; 525 res.end(); 526 } 527 528 template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app) 529 { 530 531 // allowed only for admin 532 BMCWEB_ROUTE(app, "/ibm/v1/") 533 .requires({"ConfigureComponents", "ConfigureManager"}) 534 .methods("GET"_method)( 535 [](const crow::Request &req, crow::Response &res) { 536 res.jsonValue["@odata.type"] = 537 "#ibmServiceRoot.v1_0_0.ibmServiceRoot"; 538 res.jsonValue["@odata.id"] = "/ibm/v1/"; 539 res.jsonValue["Id"] = "IBM Rest RootService"; 540 res.jsonValue["Name"] = "IBM Service Root"; 541 res.jsonValue["ConfigFiles"] = { 542 {"@odata.id", "/ibm/v1/Host/ConfigFiles"}}; 543 res.jsonValue["LockService"] = { 544 {"@odata.id", "/ibm/v1/HMC/LockService"}}; 545 res.end(); 546 }); 547 548 BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles") 549 .requires({"ConfigureComponents", "ConfigureManager"}) 550 .methods("GET"_method)( 551 [](const crow::Request &req, crow::Response &res) { 552 handleConfigFileList(res); 553 }); 554 555 BMCWEB_ROUTE(app, 556 "/ibm/v1/Host/ConfigFiles/Actions/FileCollection.DeleteAll") 557 .requires({"ConfigureComponents", "ConfigureManager"}) 558 .methods("POST"_method)( 559 [](const crow::Request &req, crow::Response &res) { 560 deleteConfigFiles(res); 561 }); 562 563 BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles/<path>") 564 .requires({"ConfigureComponents", "ConfigureManager"}) 565 .methods("PUT"_method, "GET"_method, "DELETE"_method)( 566 [](const crow::Request &req, crow::Response &res, 567 const std::string &path) { handleFileUrl(req, res, path); }); 568 569 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService") 570 .requires({"ConfigureComponents", "ConfigureManager"}) 571 .methods("GET"_method)( 572 [](const crow::Request &req, crow::Response &res) { 573 getLockServiceData(res); 574 }); 575 576 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock") 577 .requires({"ConfigureComponents", "ConfigureManager"}) 578 .methods("POST"_method)( 579 [](const crow::Request &req, crow::Response &res) { 580 std::vector<nlohmann::json> body; 581 if (!redfish::json_util::readJson(req, res, "Request", body)) 582 { 583 BMCWEB_LOG_DEBUG << "Not a Valid JSON"; 584 res.result(boost::beast::http::status::bad_request); 585 res.end(); 586 return; 587 } 588 handleAcquireLockAPI(req, res, body); 589 }); 590 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock") 591 .requires({"ConfigureComponents", "ConfigureManager"}) 592 .methods( 593 "POST"_method)([](const crow::Request &req, crow::Response &res) { 594 std::string type; 595 std::vector<uint32_t> listTransactionIds; 596 597 if (!redfish::json_util::readJson(req, res, "Type", type, 598 "TransactionIDs", 599 listTransactionIds)) 600 { 601 res.result(boost::beast::http::status::bad_request); 602 res.end(); 603 return; 604 } 605 if (type == "Transaction") 606 { 607 handleReleaseLockAPI(req, res, listTransactionIds); 608 } 609 else if (type == "Session") 610 { 611 handleRelaseAllAPI(req, res); 612 } 613 else 614 { 615 BMCWEB_LOG_DEBUG << " Value of Type : " << type 616 << "is Not a Valid key"; 617 redfish::messages::propertyValueNotInList(res, type, "Type"); 618 } 619 }); 620 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.GetLockList") 621 .requires({"ConfigureComponents", "ConfigureManager"}) 622 .methods("POST"_method)( 623 [](const crow::Request &req, crow::Response &res) { 624 ListOfSessionIds listSessionIds; 625 626 if (!redfish::json_util::readJson(req, res, "SessionIDs", 627 listSessionIds)) 628 { 629 res.result(boost::beast::http::status::bad_request); 630 res.end(); 631 return; 632 } 633 handleGetLockListAPI(req, res, listSessionIds); 634 }); 635 } 636 637 } // namespace ibm_mc 638 } // namespace crow 639