xref: /openbmc/bmcweb/include/ibm/management_console_rest.hpp (revision db81c076c588497da718673ae6d952d348cccc02)
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