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