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