blob: 305479187405eded6bcdac5fd585adc5e6c800e2 [file] [log] [blame]
manojkiraneda0b631ae2019-12-03 17:54:28 +05301#pragma once
2
manojkiraneda0b631ae2019-12-03 17:54:28 +05303#include <boost/algorithm/string.hpp>
4#include <boost/container/flat_map.hpp>
Manojkiran Eda55fd1a92020-04-30 19:06:48 +05305#include <boost/endian/conversion.hpp>
Ed Tanous04e438c2020-10-03 08:06:26 -07006#include <logging.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05007#include <nlohmann/json.hpp>
8
manojkiraneda0b631ae2019-12-03 17:54:28 +05309#include <filesystem>
Sunitha Harish8a3bb712019-12-13 03:48:09 -060010#include <fstream>
manojkiraneda0b631ae2019-12-03 17:54:28 +053011
12namespace crow
13{
14namespace ibm_mc_lock
15{
16
manojkiraneda0b631ae2019-12-03 17:54:28 +053017using SType = std::string;
18
19/*----------------------------------------
20|Segment flags : LockFlag | SegmentLength|
21------------------------------------------*/
22
23using SegmentFlags = std::vector<std::pair<SType, uint32_t>>;
24
25// Lockrequest = session-id | hmc-id | locktype | resourceid | segmentinfo
26using LockRequest = std::tuple<SType, SType, SType, uint64_t, SegmentFlags>;
manojkiraneda0b631ae2019-12-03 17:54:28 +053027using LockRequests = std::vector<LockRequest>;
28using Rc =
29 std::pair<bool, std::variant<uint32_t, std::pair<uint32_t, LockRequest>>>;
manojkiraneda3b6dea62019-12-13 17:05:36 +053030using RcRelaseLock = std::pair<bool, std::pair<uint32_t, LockRequest>>;
manojkiraneda402b5712019-12-13 17:07:09 +053031using RcGetLockList =
32 std::variant<std::string, std::vector<std::pair<uint32_t, LockRequests>>>;
manojkiraneda3b6dea62019-12-13 17:05:36 +053033using ListOfTransactionIds = std::vector<uint32_t>;
manojkiraneda0b631ae2019-12-03 17:54:28 +053034using RcAcquireLock = std::pair<bool, std::variant<Rc, std::pair<bool, int>>>;
manojkiraneda3b6dea62019-12-13 17:05:36 +053035using RcReleaseLockApi = std::pair<bool, std::variant<bool, RcRelaseLock>>;
36using SessionFlags = std::pair<SType, SType>;
manojkiraneda402b5712019-12-13 17:07:09 +053037using ListOfSessionIds = std::vector<std::string>;
Gunnar Mills1214b7e2020-06-04 10:11:30 -050038static constexpr const char* fileName =
Sunitha Harish8a3bb712019-12-13 03:48:09 -060039 "/var/lib/obmc/bmc-console-mgmt/locks/ibm_mc_persistent_lock_data.json";
manojkiraneda0b631ae2019-12-03 17:54:28 +053040
41class Lock
42{
43 uint32_t transactionId;
44 boost::container::flat_map<uint32_t, LockRequests> lockTable;
45
46 /*
Sunitha Harish8a3bb712019-12-13 03:48:09 -060047 * This API implements the logic to persist the locks that are contained in
48 * the lock table into a json file.
49 */
50 void saveLocks();
51
52 /*
53 * This API implements the logic to load the locks that are present in the
54 * json file into the lock table.
55 */
56 void loadLocks();
57
manojkiraneda4eaf2ee2019-12-13 17:10:41 +053058 bool createPersistentLockFilePath();
59
60 protected:
61 /*
62 * This function implements the logic for validating an incoming
63 * lock request/requests.
64 *
65 * Returns : True (if Valid)
66 * Returns : False (if not a Valid lock request)
67 */
68
Ed Tanousb5a76932020-09-29 16:16:58 -070069 virtual bool isValidLockRequest(const LockRequest&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +053070
71 /*
72 * This function implements the logic of checking if the incoming
73 * multi-lock request is not having conflicting requirements.
74 *
75 * Returns : True (if conflicting)
76 * Returns : False (if not conflicting)
77 */
78
Ed Tanousb5a76932020-09-29 16:16:58 -070079 virtual bool isConflictRequest(const LockRequests&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +053080 /*
81 * Implements the core algorithm to find the conflicting
82 * lock requests.
83 *
84 * This functions takes two lock requests and check if both
85 * are conflicting to each other.
86 *
87 * Returns : True (if conflicting)
88 * Returns : False (if not conflicting)
89 */
Ed Tanousb5a76932020-09-29 16:16:58 -070090 virtual bool isConflictRecord(const LockRequest&, const LockRequest&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +053091
92 /*
93 * This function implements the logic of checking the conflicting
94 * locks from a incoming single/multi lock requests with the already
95 * existing lock request in the lock table.
96 *
97 */
98
Ed Tanousb5a76932020-09-29 16:16:58 -070099 virtual Rc isConflictWithTable(const LockRequests&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530100 /*
101 * This function implements the logic of checking the ownership of the
102 * lock from the releaselock request.
103 *
104 * Returns : True (if the requesting HMC & Session owns the lock(s))
105 * Returns : False (if the request HMC or Session does not own the lock(s))
106 */
107
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500108 virtual RcRelaseLock isItMyLock(const ListOfTransactionIds&,
109 const SessionFlags&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530110
111 /*
112 * This function validates the the list of transactionID's and returns false
113 * if the transaction ID is not valid & not present in the lock table
114 */
115
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500116 virtual bool validateRids(const ListOfTransactionIds&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530117
118 /*
119 * This function releases the locks that are already obtained by the
120 * requesting Management console.
121 */
122
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500123 void releaseLock(const ListOfTransactionIds&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530124
125 Lock()
126 {
127 loadLocks();
128 transactionId = lockTable.empty() ? 0 : prev(lockTable.end())->first;
129 }
130
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600131 /*
manojkiraneda0b631ae2019-12-03 17:54:28 +0530132 * This function implements the algorithm for checking the respective
133 * bytes of the resource id based on the lock management algorithm.
134 */
135
136 bool checkByte(uint64_t, uint64_t, uint32_t);
137
138 /*
139 * This functions implements a counter that generates a unique 32 bit
140 * number for every successful transaction. This number will be used by
141 * the Management Console for debug.
142 */
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530143 virtual uint32_t generateTransactionId();
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600144
manojkiraneda0b631ae2019-12-03 17:54:28 +0530145 public:
146 /*
147 * This function implements the logic for acquiring a lock on a
Manojkiran Eda5bb0ece2020-01-20 20:22:36 +0530148 * resource if the incoming request is legitimate without any
manojkiraneda0b631ae2019-12-03 17:54:28 +0530149 * conflicting requirements & without any conflicting requirement
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500150 * with the existing locks in the lock table.
manojkiraneda0b631ae2019-12-03 17:54:28 +0530151 *
152 */
153
Ed Tanousb5a76932020-09-29 16:16:58 -0700154 RcAcquireLock acquireLock(const LockRequests&);
manojkiraneda0b631ae2019-12-03 17:54:28 +0530155
manojkiraneda3b6dea62019-12-13 17:05:36 +0530156 /*
157 * This function implements the logic for releasing the lock that are
158 * owned by a management console session.
159 *
160 * The locks can be released by two ways
161 * - Using list of transaction ID's
162 * - Using a Session ID
163 *
164 * Client can choose either of the ways by using `Type` JSON key.
165 *
166 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500167 RcReleaseLockApi releaseLock(const ListOfTransactionIds&,
168 const SessionFlags&);
manojkiraneda3b6dea62019-12-13 17:05:36 +0530169
manojkiraneda402b5712019-12-13 17:07:09 +0530170 /*
171 * This function implements the logic for getting the list of locks obtained
172 * by a particular management console.
173 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500174 RcGetLockList getLockList(const ListOfSessionIds&);
manojkiraneda402b5712019-12-13 17:07:09 +0530175
Ratan Gupta07386c62019-12-14 14:06:09 +0530176 /*
177 * This function is releases all the locks obtained by a particular
178 * session.
179 */
180
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500181 void releaseLock(const std::string&);
Ratan Gupta07386c62019-12-14 14:06:09 +0530182
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500183 static Lock& getInstance()
Ratan Gupta07386c62019-12-14 14:06:09 +0530184 {
185 static Lock lockObject;
186 return lockObject;
187 }
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530188
Ed Tanous4e087512020-09-28 18:41:25 -0700189 virtual ~Lock() = default;
Ratan Gupta07386c62019-12-14 14:06:09 +0530190};
manojkiraneda0b631ae2019-12-03 17:54:28 +0530191
Ratan Gupta07386c62019-12-14 14:06:09 +0530192inline bool Lock::createPersistentLockFilePath()
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600193{
194 // The path /var/lib/obmc will be created by initrdscripts
195 // Create the directories for the persistent lock file
196 std::error_code ec;
197 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt", ec))
198 {
199 std::filesystem::create_directory("/var/lib/obmc/bmc-console-mgmt", ec);
200 }
201 if (ec)
202 {
203 BMCWEB_LOG_DEBUG
204 << "Failed to prepare bmc-console-mgmt directory. ec : " << ec;
205 return false;
206 }
207
208 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt/locks",
209 ec))
210 {
211 std::filesystem::create_directory(
212 "/var/lib/obmc/bmc-console-mgmt/locks", ec);
213 }
214 if (ec)
215 {
216 BMCWEB_LOG_DEBUG
217 << "Failed to prepare persistent lock file directory. ec : " << ec;
218 return false;
219 }
220 return true;
221}
222
Ratan Gupta07386c62019-12-14 14:06:09 +0530223inline void Lock::loadLocks()
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600224{
225 std::ifstream persistentFile(fileName);
226 if (persistentFile.is_open())
227 {
228 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
229 if (data.is_discarded())
230 {
231 BMCWEB_LOG_ERROR << "Error parsing persistent data in json file.";
232 return;
233 }
234 BMCWEB_LOG_DEBUG << "The persistent lock data is available";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500235 for (const auto& item : data.items())
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600236 {
237 BMCWEB_LOG_DEBUG << item.key();
238 BMCWEB_LOG_DEBUG << item.value();
239 LockRequests locks = item.value();
Ratan Gupta07386c62019-12-14 14:06:09 +0530240 lockTable.emplace(std::pair<uint32_t, LockRequests>(
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600241 std::stoul(item.key()), locks));
242 BMCWEB_LOG_DEBUG << "The persistent lock data loaded";
243 }
244 }
245}
246
Ratan Gupta07386c62019-12-14 14:06:09 +0530247inline void Lock::saveLocks()
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600248{
249 std::error_code ec;
250 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt/locks",
251 ec))
252 {
253 if (!createPersistentLockFilePath())
254 {
255 BMCWEB_LOG_DEBUG << "Failed to create lock persistent path";
256 return;
257 }
258 }
259 std::ofstream persistentFile(fileName);
260 // set the permission of the file to 640
Ed Tanousb5a76932020-09-29 16:16:58 -0700261 std::filesystem::perms permission = std::filesystem::perms::owner_read |
262 std::filesystem::perms::owner_write |
263 std::filesystem::perms::group_read;
264 std::filesystem::permissions(fileName, permission);
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600265 nlohmann::json data;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500266 for (const auto& it : lockTable)
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600267 {
268 data[std::to_string(it.first)] = it.second;
269 }
270 BMCWEB_LOG_DEBUG << "data is " << data;
271 persistentFile << data;
272}
273
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500274inline RcGetLockList Lock::getLockList(const ListOfSessionIds& listSessionId)
manojkiraneda402b5712019-12-13 17:07:09 +0530275{
276
277 std::vector<std::pair<uint32_t, LockRequests>> lockList;
278
279 if (!lockTable.empty())
280 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500281 for (const auto& i : listSessionId)
manojkiraneda402b5712019-12-13 17:07:09 +0530282 {
283 auto it = lockTable.begin();
284 while (it != lockTable.end())
285 {
286 // Check if session id of this entry matches with session id
287 // given
288 if (std::get<0>(it->second[0]) == i)
289 {
290 BMCWEB_LOG_DEBUG << "Session id is found in the locktable";
291
292 // Push the whole lock record into a vector for returning
293 // the json
Ed Tanous4e087512020-09-28 18:41:25 -0700294 lockList.emplace_back(it->first, it->second);
manojkiraneda402b5712019-12-13 17:07:09 +0530295 }
296 // Go to next entry in map
297 it++;
298 }
299 }
300 }
301 // we may have found at least one entry with the given session id
302 // return the json list of lock records pertaining to the given
303 // session id, or send an empty list if lock table is empty
Ed Tanous02379d32020-09-15 21:15:44 -0700304 return {lockList};
manojkiraneda402b5712019-12-13 17:07:09 +0530305}
306
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500307inline RcReleaseLockApi Lock::releaseLock(const ListOfTransactionIds& p,
308 const SessionFlags& ids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530309{
310
311 bool status = validateRids(p);
312
313 if (!status)
314 {
315 // Validation of rids failed
316 BMCWEB_LOG_DEBUG << "Not a Valid request id";
317 return std::make_pair(false, status);
318 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700319 // Validation passed, check if all the locks are owned by the
320 // requesting HMC
321 auto status2 = isItMyLock(p, ids);
322 if (status2.first)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530323 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700324 // The current hmc owns all the locks, so we can release
325 // them
326 releaseLock(p);
manojkiraneda3b6dea62019-12-13 17:05:36 +0530327 }
Manojkiran Edaa1ffbb82020-10-28 17:42:21 +0530328 return std::make_pair(true, status2);
manojkiraneda3b6dea62019-12-13 17:05:36 +0530329}
330
Ed Tanousb5a76932020-09-29 16:16:58 -0700331inline RcAcquireLock Lock::acquireLock(const LockRequests& lockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530332{
333
334 // validate the lock request
335
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500336 for (auto& lockRecord : lockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530337 {
338 bool status = isValidLockRequest(lockRecord);
339 if (!status)
340 {
341 BMCWEB_LOG_DEBUG << "Not a Valid record";
342 BMCWEB_LOG_DEBUG << "Bad json in request";
343 return std::make_pair(true, std::make_pair(status, 0));
344 }
345 }
346 // check for conflict record
347
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500348 const LockRequests& multiRequest = lockRequestStructure;
manojkiraneda0b631ae2019-12-03 17:54:28 +0530349 bool status = isConflictRequest(multiRequest);
350
351 if (status)
352 {
353 BMCWEB_LOG_DEBUG << "There is a conflict within itself";
354 return std::make_pair(true, std::make_pair(status, 1));
355 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700356 BMCWEB_LOG_DEBUG << "The request is not conflicting within itself";
manojkiraneda0b631ae2019-12-03 17:54:28 +0530357
Ed Tanous3174e4d2020-10-07 11:41:22 -0700358 // Need to check for conflict with the locktable entries.
manojkiraneda0b631ae2019-12-03 17:54:28 +0530359
Ed Tanous3174e4d2020-10-07 11:41:22 -0700360 auto conflict = isConflictWithTable(multiRequest);
manojkiraneda0b631ae2019-12-03 17:54:28 +0530361
Ed Tanous3174e4d2020-10-07 11:41:22 -0700362 BMCWEB_LOG_DEBUG << "Done with checking conflict with the locktable";
363 return std::make_pair(false, conflict);
manojkiraneda0b631ae2019-12-03 17:54:28 +0530364}
365
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500366inline void Lock::releaseLock(const ListOfTransactionIds& refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530367{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500368 for (const auto& id : refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530369 {
370 if (lockTable.erase(id))
371 {
372 BMCWEB_LOG_DEBUG << "Removing the locks with transaction ID : "
373 << id;
374 }
375
376 else
377 {
378 BMCWEB_LOG_DEBUG << "Removing the locks from the lock table "
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500379 "failed, transaction ID: "
manojkiraneda3b6dea62019-12-13 17:05:36 +0530380 << id;
381 }
382 }
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600383
384 saveLocks();
manojkiraneda3b6dea62019-12-13 17:05:36 +0530385}
386
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500387inline void Lock::releaseLock(const std::string& sessionId)
Ratan Gupta07386c62019-12-14 14:06:09 +0530388{
389 bool isErased = false;
390 if (!lockTable.empty())
391 {
392 auto it = lockTable.begin();
393 while (it != lockTable.end())
394 {
395 if (it->second.size() != 0)
396 {
397 // Check if session id of this entry matches with session id
398 // given
399 if (std::get<0>(it->second[0]) == sessionId)
400 {
401 BMCWEB_LOG_DEBUG << "Remove the lock from the locktable "
402 "having sessionID="
403 << sessionId;
404 BMCWEB_LOG_DEBUG << "TransactionID =" << it->first;
405 it = lockTable.erase(it);
406 isErased = true;
407 }
408 else
409 {
410 it++;
411 }
412 }
413 }
414 if (isErased)
415 {
416 // save the lock in the persistent file
417 saveLocks();
418 }
419 }
420}
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500421inline RcRelaseLock Lock::isItMyLock(const ListOfTransactionIds& refRids,
422 const SessionFlags& ids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530423{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500424 for (const auto& id : refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530425 {
426 // Just need to compare the client id of the first lock records in the
427 // complete lock row(in the map), because the rest of the lock records
428 // would have the same client id
429
430 std::string expectedClientId = std::get<1>(lockTable[id][0]);
431 std::string expectedSessionId = std::get<0>(lockTable[id][0]);
432
433 if ((expectedClientId == ids.first) &&
434 (expectedSessionId == ids.second))
435 {
436 // It is owned by the currently request hmc
437 BMCWEB_LOG_DEBUG << "Lock is owned by the current hmc";
438 }
439 else
440 {
441 BMCWEB_LOG_DEBUG << "Lock is not owned by the current hmc";
442 return std::make_pair(false, std::make_pair(id, lockTable[id][0]));
443 }
444 }
445 return std::make_pair(true, std::make_pair(0, LockRequest()));
446}
447
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500448inline bool Lock::validateRids(const ListOfTransactionIds& refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530449{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500450 for (const auto& id : refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530451 {
452 auto search = lockTable.find(id);
453
454 if (search != lockTable.end())
455 {
456 BMCWEB_LOG_DEBUG << "Valid Transaction id";
457 // continue for the next rid
458 }
459 else
460 {
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500461 BMCWEB_LOG_DEBUG << "At least 1 inValid Request id";
manojkiraneda3b6dea62019-12-13 17:05:36 +0530462 return false;
463 }
464 }
465 return true;
466}
467
Ed Tanousb5a76932020-09-29 16:16:58 -0700468inline bool Lock::isValidLockRequest(const LockRequest& refLockRecord)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530469{
470
471 // validate the locktype
472
473 if (!((boost::equals(std::get<2>(refLockRecord), "Read") ||
474 (boost::equals(std::get<2>(refLockRecord), "Write")))))
475 {
476 BMCWEB_LOG_DEBUG << "Validation of LockType Failed";
477 BMCWEB_LOG_DEBUG << "Locktype : " << std::get<2>(refLockRecord);
478 return false;
479 }
480
481 BMCWEB_LOG_DEBUG << static_cast<int>(std::get<4>(refLockRecord).size());
482
483 // validate the number of segments
484 // Allowed No of segments are between 2 and 6
485 if ((static_cast<int>(std::get<4>(refLockRecord).size()) > 6) ||
486 (static_cast<int>(std::get<4>(refLockRecord).size()) < 2))
487 {
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500488 BMCWEB_LOG_DEBUG << "Validation of Number of Segments Failed";
manojkiraneda0b631ae2019-12-03 17:54:28 +0530489 BMCWEB_LOG_DEBUG << "Number of Segments provied : "
Ed Tanous7cd94e42020-09-29 16:03:02 -0700490 << std::get<4>(refLockRecord).size();
manojkiraneda0b631ae2019-12-03 17:54:28 +0530491 return false;
492 }
493
494 int lockFlag = 0;
495 // validate the lockflags & segment length
496
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500497 for (const auto& p : std::get<4>(refLockRecord))
manojkiraneda0b631ae2019-12-03 17:54:28 +0530498 {
499
500 // validate the lock flags
501 // Allowed lockflags are locksame,lockall & dontlock
502
503 if (!((boost::equals(p.first, "LockSame") ||
504 (boost::equals(p.first, "LockAll")) ||
505 (boost::equals(p.first, "DontLock")))))
506 {
507 BMCWEB_LOG_DEBUG << "Validation of lock flags failed";
508 BMCWEB_LOG_DEBUG << p.first;
509 return false;
510 }
511
512 // validate the segment length
513 // Allowed values of segment length are between 1 and 4
514
515 if (p.second < 1 || p.second > 4)
516 {
517 BMCWEB_LOG_DEBUG << "Validation of Segment Length Failed";
518 BMCWEB_LOG_DEBUG << p.second;
519 return false;
520 }
521
522 if ((boost::equals(p.first, "LockSame") ||
523 (boost::equals(p.first, "LockAll"))))
524 {
525 ++lockFlag;
526 if (lockFlag >= 2)
527 {
528 return false;
529 }
530 }
531 }
532
manojkiraneda0b631ae2019-12-03 17:54:28 +0530533 return true;
534}
535
Ed Tanousb5a76932020-09-29 16:16:58 -0700536inline Rc Lock::isConflictWithTable(const LockRequests& refLockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530537{
538
539 uint32_t transactionId;
540
541 if (lockTable.empty())
542 {
543 transactionId = generateTransactionId();
544 BMCWEB_LOG_DEBUG << transactionId;
545 // Lock table is empty, so we are safe to add the lockrecords
546 // as there will be no conflict
547 BMCWEB_LOG_DEBUG << "Lock table is empty, so adding the lockrecords";
548 lockTable.emplace(std::pair<uint32_t, LockRequests>(
549 transactionId, refLockRequestStructure));
550
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600551 // save the lock in the persistent file
552 saveLocks();
manojkiraneda0b631ae2019-12-03 17:54:28 +0530553 return std::make_pair(false, transactionId);
554 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700555 BMCWEB_LOG_DEBUG
556 << "Lock table is not empty, check for conflict with lock table";
557 // Lock table is not empty, compare the lockrequest entries with
558 // the entries in the lock table
manojkiraneda0b631ae2019-12-03 17:54:28 +0530559
Ed Tanous3174e4d2020-10-07 11:41:22 -0700560 for (const auto& lockRecord1 : refLockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530561 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700562 for (const auto& map : lockTable)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530563 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700564 for (const auto& lockRecord2 : map.second)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530565 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700566 bool status = isConflictRecord(lockRecord1, lockRecord2);
567 if (status)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530568 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700569 return std::make_pair(
570 true, std::make_pair(map.first, lockRecord2));
manojkiraneda0b631ae2019-12-03 17:54:28 +0530571 }
572 }
573 }
manojkiraneda0b631ae2019-12-03 17:54:28 +0530574 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700575
576 // Reached here, so no conflict with the locktable, so we are safe to
577 // add the request records into the lock table
578
579 // Lock table is empty, so we are safe to add the lockrecords
580 // as there will be no conflict
581 BMCWEB_LOG_DEBUG << " Adding elements into lock table";
582 transactionId = generateTransactionId();
583 lockTable.emplace(std::make_pair(transactionId, refLockRequestStructure));
584
585 // save the lock in the persistent file
586 saveLocks();
manojkiraneda0b631ae2019-12-03 17:54:28 +0530587 return std::make_pair(false, transactionId);
588}
589
Ed Tanousb5a76932020-09-29 16:16:58 -0700590inline bool Lock::isConflictRequest(const LockRequests& refLockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530591{
592 // check for all the locks coming in as a part of single request
593 // return conflict if any two lock requests are conflicting
594
595 if (refLockRequestStructure.size() == 1)
596 {
597 BMCWEB_LOG_DEBUG << "Only single lock request, so there is no conflict";
598 // This means , we have only one lock request in the current
599 // request , so no conflict within the request
600 return false;
601 }
602
Ed Tanous3174e4d2020-10-07 11:41:22 -0700603 BMCWEB_LOG_DEBUG
604 << "There are multiple lock requests coming in a single request";
605
606 // There are multiple requests a part of one request
607
608 for (uint32_t i = 0; i < refLockRequestStructure.size(); i++)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530609 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700610 for (uint32_t j = i + 1; j < refLockRequestStructure.size(); j++)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530611 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700612 const LockRequest& p = refLockRequestStructure[i];
613 const LockRequest& q = refLockRequestStructure[j];
614 bool status = isConflictRecord(p, q);
manojkiraneda0b631ae2019-12-03 17:54:28 +0530615
Ed Tanous3174e4d2020-10-07 11:41:22 -0700616 if (status)
617 {
618 return true;
manojkiraneda0b631ae2019-12-03 17:54:28 +0530619 }
620 }
621 }
622 return false;
623}
624
625// This function converts the provided uint64_t resource id's from the two
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500626// lock requests subjected for comparison, and this function also compares
manojkiraneda0b631ae2019-12-03 17:54:28 +0530627// the content by bytes mentioned by a uint32_t number.
628
629// If all the elements in the lock requests which are subjected for comparison
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500630// are same, then the last comparison would be to check for the respective
manojkiraneda0b631ae2019-12-03 17:54:28 +0530631// bytes in the resourceid based on the segment length.
632
Ratan Gupta07386c62019-12-14 14:06:09 +0530633inline bool Lock::checkByte(uint64_t resourceId1, uint64_t resourceId2,
634 uint32_t position)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530635{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500636 uint8_t* p = reinterpret_cast<uint8_t*>(&resourceId1);
637 uint8_t* q = reinterpret_cast<uint8_t*>(&resourceId2);
manojkiraneda0b631ae2019-12-03 17:54:28 +0530638
639 BMCWEB_LOG_DEBUG << "Comparing bytes " << std::to_string(p[position]) << ","
640 << std::to_string(q[position]);
641 if (p[position] != q[position])
642 {
643 return false;
644 }
645
manojkiraneda0b631ae2019-12-03 17:54:28 +0530646 return true;
647}
648
Ed Tanousb5a76932020-09-29 16:16:58 -0700649inline bool Lock::isConflictRecord(const LockRequest& refLockRecord1,
650 const LockRequest& refLockRecord2)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530651{
652 // No conflict if both are read locks
653
654 if (boost::equals(std::get<2>(refLockRecord1), "Read") &&
655 boost::equals(std::get<2>(refLockRecord2), "Read"))
656 {
657 BMCWEB_LOG_DEBUG << "Both are read locks, no conflict";
658 return false;
659 }
660
Ed Tanous3174e4d2020-10-07 11:41:22 -0700661 uint32_t i = 0;
662 for (const auto& p : std::get<4>(refLockRecord1))
manojkiraneda0b631ae2019-12-03 17:54:28 +0530663 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700664
665 // return conflict when any of them is try to lock all resources
666 // under the current resource level.
667 if (boost::equals(p.first, "LockAll") ||
668 boost::equals(std::get<4>(refLockRecord2)[i].first, "LockAll"))
manojkiraneda0b631ae2019-12-03 17:54:28 +0530669 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700670 BMCWEB_LOG_DEBUG
671 << "Either of the Comparing locks are trying to lock all "
672 "resources under the current resource level";
673 return true;
674 }
manojkiraneda0b631ae2019-12-03 17:54:28 +0530675
Ed Tanous3174e4d2020-10-07 11:41:22 -0700676 // determine if there is a lock-all-with-same-segment-size.
677 // If the current segment sizes are the same,then we should fail.
678
679 if ((boost::equals(p.first, "LockSame") ||
680 boost::equals(std::get<4>(refLockRecord2)[i].first, "LockSame")) &&
681 (p.second == std::get<4>(refLockRecord2)[i].second))
682 {
683 return true;
684 }
685
686 // if segment lengths are not the same, it means two different locks
687 // So no conflict
688 if (p.second != std::get<4>(refLockRecord2)[i].second)
689 {
690 BMCWEB_LOG_DEBUG << "Segment lengths are not same";
691 BMCWEB_LOG_DEBUG << "Segment 1 length : " << p.second;
692 BMCWEB_LOG_DEBUG << "Segment 2 length : "
693 << std::get<4>(refLockRecord2)[i].second;
694 return false;
695 }
696
697 // compare segment data
698
699 for (uint32_t i = 0; i < p.second; i++)
700 {
701 // if the segment data is different , then the locks is on a
702 // different resource So no conflict between the lock
703 // records.
704 // BMC is little endian , but the resourceID is formed by
705 // the Management Console in such a way that, the first byte
706 // from the MSB Position corresponds to the First Segment
707 // data. Therefore we need to convert the in-comming
708 // resourceID into Big Endian before processing further.
709 if (!(checkByte(
710 boost::endian::endian_reverse(std::get<3>(refLockRecord1)),
711 boost::endian::endian_reverse(std::get<3>(refLockRecord2)),
712 i)))
manojkiraneda0b631ae2019-12-03 17:54:28 +0530713 {
manojkiraneda0b631ae2019-12-03 17:54:28 +0530714 return false;
715 }
manojkiraneda0b631ae2019-12-03 17:54:28 +0530716 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700717
718 ++i;
manojkiraneda0b631ae2019-12-03 17:54:28 +0530719 }
720
721 return false;
722}
723
Ratan Gupta07386c62019-12-14 14:06:09 +0530724inline uint32_t Lock::generateTransactionId()
manojkiraneda0b631ae2019-12-03 17:54:28 +0530725{
726 ++transactionId;
727 return transactionId;
728}
729
730} // namespace ibm_mc_lock
731} // namespace crow