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