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