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