blob: f6719c7caad92e91ac647d4a52320ce88cb853e9 [file] [log] [blame]
manojkiraneda0b631ae2019-12-03 17:54:28 +05301#pragma once
2
manojkiraneda4eaf2ee2019-12-13 17:10:41 +05303#include <logging.h>
4
manojkiraneda0b631ae2019-12-03 17:54:28 +05305#include <boost/algorithm/string.hpp>
6#include <boost/container/flat_map.hpp>
Manojkiran Eda55fd1a92020-04-30 19:06:48 +05307#include <boost/endian/conversion.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05008#include <nlohmann/json.hpp>
9
manojkiraneda0b631ae2019-12-03 17:54:28 +053010#include <filesystem>
Sunitha Harish8a3bb712019-12-13 03:48:09 -060011#include <fstream>
manojkiraneda0b631ae2019-12-03 17:54:28 +053012
13namespace crow
14{
15namespace ibm_mc_lock
16{
17
18namespace fs = std::filesystem;
19using SType = std::string;
20
21/*----------------------------------------
22|Segment flags : LockFlag | SegmentLength|
23------------------------------------------*/
24
25using SegmentFlags = std::vector<std::pair<SType, uint32_t>>;
26
27// Lockrequest = session-id | hmc-id | locktype | resourceid | segmentinfo
28using LockRequest = std::tuple<SType, SType, SType, uint64_t, SegmentFlags>;
manojkiraneda0b631ae2019-12-03 17:54:28 +053029using LockRequests = std::vector<LockRequest>;
30using Rc =
31 std::pair<bool, std::variant<uint32_t, std::pair<uint32_t, LockRequest>>>;
manojkiraneda3b6dea62019-12-13 17:05:36 +053032using RcRelaseLock = std::pair<bool, std::pair<uint32_t, LockRequest>>;
manojkiraneda402b5712019-12-13 17:07:09 +053033using RcGetLockList =
34 std::variant<std::string, std::vector<std::pair<uint32_t, LockRequests>>>;
manojkiraneda3b6dea62019-12-13 17:05:36 +053035using ListOfTransactionIds = std::vector<uint32_t>;
manojkiraneda0b631ae2019-12-03 17:54:28 +053036using RcAcquireLock = std::pair<bool, std::variant<Rc, std::pair<bool, int>>>;
manojkiraneda3b6dea62019-12-13 17:05:36 +053037using RcReleaseLockApi = std::pair<bool, std::variant<bool, RcRelaseLock>>;
38using SessionFlags = std::pair<SType, SType>;
manojkiraneda402b5712019-12-13 17:07:09 +053039using ListOfSessionIds = std::vector<std::string>;
Gunnar Mills1214b7e2020-06-04 10:11:30 -050040static constexpr const char* fileName =
Sunitha Harish8a3bb712019-12-13 03:48:09 -060041 "/var/lib/obmc/bmc-console-mgmt/locks/ibm_mc_persistent_lock_data.json";
manojkiraneda0b631ae2019-12-03 17:54:28 +053042
43class Lock
44{
45 uint32_t transactionId;
46 boost::container::flat_map<uint32_t, LockRequests> lockTable;
47
48 /*
Sunitha Harish8a3bb712019-12-13 03:48:09 -060049 * This API implements the logic to persist the locks that are contained in
50 * the lock table into a json file.
51 */
52 void saveLocks();
53
54 /*
55 * This API implements the logic to load the locks that are present in the
56 * json file into the lock table.
57 */
58 void loadLocks();
59
manojkiraneda4eaf2ee2019-12-13 17:10:41 +053060 bool createPersistentLockFilePath();
61
62 protected:
63 /*
64 * This function implements the logic for validating an incoming
65 * lock request/requests.
66 *
67 * Returns : True (if Valid)
68 * Returns : False (if not a Valid lock request)
69 */
70
71 virtual bool isValidLockRequest(const LockRequest);
72
73 /*
74 * This function implements the logic of checking if the incoming
75 * multi-lock request is not having conflicting requirements.
76 *
77 * Returns : True (if conflicting)
78 * Returns : False (if not conflicting)
79 */
80
81 virtual bool isConflictRequest(const LockRequests);
82 /*
83 * Implements the core algorithm to find the conflicting
84 * lock requests.
85 *
86 * This functions takes two lock requests and check if both
87 * are conflicting to each other.
88 *
89 * Returns : True (if conflicting)
90 * Returns : False (if not conflicting)
91 */
92 virtual bool isConflictRecord(const LockRequest, const LockRequest);
93
94 /*
95 * This function implements the logic of checking the conflicting
96 * locks from a incoming single/multi lock requests with the already
97 * existing lock request in the lock table.
98 *
99 */
100
101 virtual Rc isConflictWithTable(const LockRequests);
102 /*
103 * This function implements the logic of checking the ownership of the
104 * lock from the releaselock request.
105 *
106 * Returns : True (if the requesting HMC & Session owns the lock(s))
107 * Returns : False (if the request HMC or Session does not own the lock(s))
108 */
109
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500110 virtual RcRelaseLock isItMyLock(const ListOfTransactionIds&,
111 const SessionFlags&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530112
113 /*
114 * This function validates the the list of transactionID's and returns false
115 * if the transaction ID is not valid & not present in the lock table
116 */
117
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500118 virtual bool validateRids(const ListOfTransactionIds&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530119
120 /*
121 * This function releases the locks that are already obtained by the
122 * requesting Management console.
123 */
124
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500125 void releaseLock(const ListOfTransactionIds&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530126
127 Lock()
128 {
129 loadLocks();
130 transactionId = lockTable.empty() ? 0 : prev(lockTable.end())->first;
131 }
132
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600133 /*
manojkiraneda0b631ae2019-12-03 17:54:28 +0530134 * This function implements the algorithm for checking the respective
135 * bytes of the resource id based on the lock management algorithm.
136 */
137
138 bool checkByte(uint64_t, uint64_t, uint32_t);
139
140 /*
141 * This functions implements a counter that generates a unique 32 bit
142 * number for every successful transaction. This number will be used by
143 * the Management Console for debug.
144 */
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530145 virtual uint32_t generateTransactionId();
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600146
manojkiraneda0b631ae2019-12-03 17:54:28 +0530147 public:
148 /*
149 * This function implements the logic for acquiring a lock on a
Manojkiran Eda5bb0ece2020-01-20 20:22:36 +0530150 * resource if the incoming request is legitimate without any
manojkiraneda0b631ae2019-12-03 17:54:28 +0530151 * conflicting requirements & without any conflicting requirement
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500152 * with the existing locks in the lock table.
manojkiraneda0b631ae2019-12-03 17:54:28 +0530153 *
154 */
155
156 RcAcquireLock acquireLock(const LockRequests);
157
manojkiraneda3b6dea62019-12-13 17:05:36 +0530158 /*
159 * This function implements the logic for releasing the lock that are
160 * owned by a management console session.
161 *
162 * The locks can be released by two ways
163 * - Using list of transaction ID's
164 * - Using a Session ID
165 *
166 * Client can choose either of the ways by using `Type` JSON key.
167 *
168 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500169 RcReleaseLockApi releaseLock(const ListOfTransactionIds&,
170 const SessionFlags&);
manojkiraneda3b6dea62019-12-13 17:05:36 +0530171
manojkiraneda402b5712019-12-13 17:07:09 +0530172 /*
173 * This function implements the logic for getting the list of locks obtained
174 * by a particular management console.
175 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500176 RcGetLockList getLockList(const ListOfSessionIds&);
manojkiraneda402b5712019-12-13 17:07:09 +0530177
Ratan Gupta07386c62019-12-14 14:06:09 +0530178 /*
179 * This function is releases all the locks obtained by a particular
180 * session.
181 */
182
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500183 void releaseLock(const std::string&);
Ratan Gupta07386c62019-12-14 14:06:09 +0530184
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500185 static Lock& getInstance()
Ratan Gupta07386c62019-12-14 14:06:09 +0530186 {
187 static Lock lockObject;
188 return lockObject;
189 }
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530190
Ed Tanous4e087512020-09-28 18:41:25 -0700191 virtual ~Lock() = default;
Ratan Gupta07386c62019-12-14 14:06:09 +0530192};
manojkiraneda0b631ae2019-12-03 17:54:28 +0530193
Ratan Gupta07386c62019-12-14 14:06:09 +0530194inline bool Lock::createPersistentLockFilePath()
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600195{
196 // The path /var/lib/obmc will be created by initrdscripts
197 // Create the directories for the persistent lock file
198 std::error_code ec;
199 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt", ec))
200 {
201 std::filesystem::create_directory("/var/lib/obmc/bmc-console-mgmt", ec);
202 }
203 if (ec)
204 {
205 BMCWEB_LOG_DEBUG
206 << "Failed to prepare bmc-console-mgmt directory. ec : " << ec;
207 return false;
208 }
209
210 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt/locks",
211 ec))
212 {
213 std::filesystem::create_directory(
214 "/var/lib/obmc/bmc-console-mgmt/locks", ec);
215 }
216 if (ec)
217 {
218 BMCWEB_LOG_DEBUG
219 << "Failed to prepare persistent lock file directory. ec : " << ec;
220 return false;
221 }
222 return true;
223}
224
Ratan Gupta07386c62019-12-14 14:06:09 +0530225inline void Lock::loadLocks()
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600226{
227 std::ifstream persistentFile(fileName);
228 if (persistentFile.is_open())
229 {
230 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
231 if (data.is_discarded())
232 {
233 BMCWEB_LOG_ERROR << "Error parsing persistent data in json file.";
234 return;
235 }
236 BMCWEB_LOG_DEBUG << "The persistent lock data is available";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500237 for (const auto& item : data.items())
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600238 {
239 BMCWEB_LOG_DEBUG << item.key();
240 BMCWEB_LOG_DEBUG << item.value();
241 LockRequests locks = item.value();
Ratan Gupta07386c62019-12-14 14:06:09 +0530242 lockTable.emplace(std::pair<uint32_t, LockRequests>(
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600243 std::stoul(item.key()), locks));
244 BMCWEB_LOG_DEBUG << "The persistent lock data loaded";
245 }
246 }
247}
248
Ratan Gupta07386c62019-12-14 14:06:09 +0530249inline void Lock::saveLocks()
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600250{
251 std::error_code ec;
252 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt/locks",
253 ec))
254 {
255 if (!createPersistentLockFilePath())
256 {
257 BMCWEB_LOG_DEBUG << "Failed to create lock persistent path";
258 return;
259 }
260 }
261 std::ofstream persistentFile(fileName);
262 // set the permission of the file to 640
263 fs::perms permission =
264 fs::perms::owner_read | fs::perms::owner_write | fs::perms::group_read;
265 fs::permissions(fileName, permission);
266 nlohmann::json data;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500267 for (const auto& it : lockTable)
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600268 {
269 data[std::to_string(it.first)] = it.second;
270 }
271 BMCWEB_LOG_DEBUG << "data is " << data;
272 persistentFile << data;
273}
274
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500275inline RcGetLockList Lock::getLockList(const ListOfSessionIds& listSessionId)
manojkiraneda402b5712019-12-13 17:07:09 +0530276{
277
278 std::vector<std::pair<uint32_t, LockRequests>> lockList;
279
280 if (!lockTable.empty())
281 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500282 for (const auto& i : listSessionId)
manojkiraneda402b5712019-12-13 17:07:09 +0530283 {
284 auto it = lockTable.begin();
285 while (it != lockTable.end())
286 {
287 // Check if session id of this entry matches with session id
288 // given
289 if (std::get<0>(it->second[0]) == i)
290 {
291 BMCWEB_LOG_DEBUG << "Session id is found in the locktable";
292
293 // Push the whole lock record into a vector for returning
294 // the json
Ed Tanous4e087512020-09-28 18:41:25 -0700295 lockList.emplace_back(it->first, it->second);
manojkiraneda402b5712019-12-13 17:07:09 +0530296 }
297 // Go to next entry in map
298 it++;
299 }
300 }
301 }
302 // we may have found at least one entry with the given session id
303 // return the json list of lock records pertaining to the given
304 // session id, or send an empty list if lock table is empty
Ed Tanous02379d32020-09-15 21:15:44 -0700305 return {lockList};
manojkiraneda402b5712019-12-13 17:07:09 +0530306}
307
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500308inline RcReleaseLockApi Lock::releaseLock(const ListOfTransactionIds& p,
309 const SessionFlags& ids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530310{
311
312 bool status = validateRids(p);
313
314 if (!status)
315 {
316 // Validation of rids failed
317 BMCWEB_LOG_DEBUG << "Not a Valid request id";
318 return std::make_pair(false, status);
319 }
320 else
321 {
322 // Validation passed, check if all the locks are owned by the
323 // requesting HMC
324 auto status = isItMyLock(p, ids);
325 if (status.first)
326 {
327 // The current hmc owns all the locks, so we can release
328 // them
329 releaseLock(p);
330 }
331 return std::make_pair(true, status);
332 }
333 return std::make_pair(false, status);
334}
335
Ratan Gupta07386c62019-12-14 14:06:09 +0530336inline RcAcquireLock Lock::acquireLock(const LockRequests lockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530337{
338
339 // validate the lock request
340
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500341 for (auto& lockRecord : lockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530342 {
343 bool status = isValidLockRequest(lockRecord);
344 if (!status)
345 {
346 BMCWEB_LOG_DEBUG << "Not a Valid record";
347 BMCWEB_LOG_DEBUG << "Bad json in request";
348 return std::make_pair(true, std::make_pair(status, 0));
349 }
350 }
351 // check for conflict record
352
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500353 const LockRequests& multiRequest = lockRequestStructure;
manojkiraneda0b631ae2019-12-03 17:54:28 +0530354 bool status = isConflictRequest(multiRequest);
355
356 if (status)
357 {
358 BMCWEB_LOG_DEBUG << "There is a conflict within itself";
359 return std::make_pair(true, std::make_pair(status, 1));
360 }
361 else
362 {
363 BMCWEB_LOG_DEBUG << "The request is not conflicting within itself";
364
365 // Need to check for conflict with the locktable entries.
366
367 auto conflict = isConflictWithTable(multiRequest);
368
369 BMCWEB_LOG_DEBUG << "Done with checking conflict with the locktable";
370 return std::make_pair(false, conflict);
371 }
372
373 return std::make_pair(true, std::make_pair(true, 1));
374}
375
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500376inline void Lock::releaseLock(const ListOfTransactionIds& refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530377{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500378 for (const auto& id : refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530379 {
380 if (lockTable.erase(id))
381 {
382 BMCWEB_LOG_DEBUG << "Removing the locks with transaction ID : "
383 << id;
384 }
385
386 else
387 {
388 BMCWEB_LOG_DEBUG << "Removing the locks from the lock table "
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500389 "failed, transaction ID: "
manojkiraneda3b6dea62019-12-13 17:05:36 +0530390 << id;
391 }
392 }
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600393
394 saveLocks();
manojkiraneda3b6dea62019-12-13 17:05:36 +0530395}
396
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500397inline void Lock::releaseLock(const std::string& sessionId)
Ratan Gupta07386c62019-12-14 14:06:09 +0530398{
399 bool isErased = false;
400 if (!lockTable.empty())
401 {
402 auto it = lockTable.begin();
403 while (it != lockTable.end())
404 {
405 if (it->second.size() != 0)
406 {
407 // Check if session id of this entry matches with session id
408 // given
409 if (std::get<0>(it->second[0]) == sessionId)
410 {
411 BMCWEB_LOG_DEBUG << "Remove the lock from the locktable "
412 "having sessionID="
413 << sessionId;
414 BMCWEB_LOG_DEBUG << "TransactionID =" << it->first;
415 it = lockTable.erase(it);
416 isErased = true;
417 }
418 else
419 {
420 it++;
421 }
422 }
423 }
424 if (isErased)
425 {
426 // save the lock in the persistent file
427 saveLocks();
428 }
429 }
430}
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500431inline RcRelaseLock Lock::isItMyLock(const ListOfTransactionIds& refRids,
432 const SessionFlags& ids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530433{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500434 for (const auto& id : refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530435 {
436 // Just need to compare the client id of the first lock records in the
437 // complete lock row(in the map), because the rest of the lock records
438 // would have the same client id
439
440 std::string expectedClientId = std::get<1>(lockTable[id][0]);
441 std::string expectedSessionId = std::get<0>(lockTable[id][0]);
442
443 if ((expectedClientId == ids.first) &&
444 (expectedSessionId == ids.second))
445 {
446 // It is owned by the currently request hmc
447 BMCWEB_LOG_DEBUG << "Lock is owned by the current hmc";
448 }
449 else
450 {
451 BMCWEB_LOG_DEBUG << "Lock is not owned by the current hmc";
452 return std::make_pair(false, std::make_pair(id, lockTable[id][0]));
453 }
454 }
455 return std::make_pair(true, std::make_pair(0, LockRequest()));
456}
457
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500458inline bool Lock::validateRids(const ListOfTransactionIds& refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530459{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500460 for (const auto& id : refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530461 {
462 auto search = lockTable.find(id);
463
464 if (search != lockTable.end())
465 {
466 BMCWEB_LOG_DEBUG << "Valid Transaction id";
467 // continue for the next rid
468 }
469 else
470 {
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500471 BMCWEB_LOG_DEBUG << "At least 1 inValid Request id";
manojkiraneda3b6dea62019-12-13 17:05:36 +0530472 return false;
473 }
474 }
475 return true;
476}
477
Ratan Gupta07386c62019-12-14 14:06:09 +0530478inline bool Lock::isValidLockRequest(const LockRequest refLockRecord)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530479{
480
481 // validate the locktype
482
483 if (!((boost::equals(std::get<2>(refLockRecord), "Read") ||
484 (boost::equals(std::get<2>(refLockRecord), "Write")))))
485 {
486 BMCWEB_LOG_DEBUG << "Validation of LockType Failed";
487 BMCWEB_LOG_DEBUG << "Locktype : " << std::get<2>(refLockRecord);
488 return false;
489 }
490
491 BMCWEB_LOG_DEBUG << static_cast<int>(std::get<4>(refLockRecord).size());
492
493 // validate the number of segments
494 // Allowed No of segments are between 2 and 6
495 if ((static_cast<int>(std::get<4>(refLockRecord).size()) > 6) ||
496 (static_cast<int>(std::get<4>(refLockRecord).size()) < 2))
497 {
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500498 BMCWEB_LOG_DEBUG << "Validation of Number of Segments Failed";
manojkiraneda0b631ae2019-12-03 17:54:28 +0530499 BMCWEB_LOG_DEBUG << "Number of Segments provied : "
Ed Tanous7cd94e42020-09-29 16:03:02 -0700500 << std::get<4>(refLockRecord).size();
manojkiraneda0b631ae2019-12-03 17:54:28 +0530501 return false;
502 }
503
504 int lockFlag = 0;
505 // validate the lockflags & segment length
506
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500507 for (const auto& p : std::get<4>(refLockRecord))
manojkiraneda0b631ae2019-12-03 17:54:28 +0530508 {
509
510 // validate the lock flags
511 // Allowed lockflags are locksame,lockall & dontlock
512
513 if (!((boost::equals(p.first, "LockSame") ||
514 (boost::equals(p.first, "LockAll")) ||
515 (boost::equals(p.first, "DontLock")))))
516 {
517 BMCWEB_LOG_DEBUG << "Validation of lock flags failed";
518 BMCWEB_LOG_DEBUG << p.first;
519 return false;
520 }
521
522 // validate the segment length
523 // Allowed values of segment length are between 1 and 4
524
525 if (p.second < 1 || p.second > 4)
526 {
527 BMCWEB_LOG_DEBUG << "Validation of Segment Length Failed";
528 BMCWEB_LOG_DEBUG << p.second;
529 return false;
530 }
531
532 if ((boost::equals(p.first, "LockSame") ||
533 (boost::equals(p.first, "LockAll"))))
534 {
535 ++lockFlag;
536 if (lockFlag >= 2)
537 {
538 return false;
539 }
540 }
541 }
542
manojkiraneda0b631ae2019-12-03 17:54:28 +0530543 return true;
544}
545
Ratan Gupta07386c62019-12-14 14:06:09 +0530546inline Rc Lock::isConflictWithTable(const LockRequests refLockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530547{
548
549 uint32_t transactionId;
550
551 if (lockTable.empty())
552 {
553 transactionId = generateTransactionId();
554 BMCWEB_LOG_DEBUG << transactionId;
555 // Lock table is empty, so we are safe to add the lockrecords
556 // as there will be no conflict
557 BMCWEB_LOG_DEBUG << "Lock table is empty, so adding the lockrecords";
558 lockTable.emplace(std::pair<uint32_t, LockRequests>(
559 transactionId, refLockRequestStructure));
560
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600561 // save the lock in the persistent file
562 saveLocks();
manojkiraneda0b631ae2019-12-03 17:54:28 +0530563 return std::make_pair(false, transactionId);
564 }
565
566 else
567 {
568 BMCWEB_LOG_DEBUG
569 << "Lock table is not empty, check for conflict with lock table";
570 // Lock table is not empty, compare the lockrequest entries with
571 // the entries in the lock table
572
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500573 for (const auto& lockRecord1 : refLockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530574 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500575 for (const auto& map : lockTable)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530576 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500577 for (const auto& lockRecord2 : map.second)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530578 {
579 bool status = isConflictRecord(lockRecord1, lockRecord2);
580 if (status)
581 {
582 return std::make_pair(
583 true, std::make_pair(map.first, lockRecord2));
584 }
585 }
586 }
587 }
588
589 // Reached here, so no conflict with the locktable, so we are safe to
590 // add the request records into the lock table
591
592 // Lock table is empty, so we are safe to add the lockrecords
593 // as there will be no conflict
594 BMCWEB_LOG_DEBUG << " Adding elements into lock table";
595 transactionId = generateTransactionId();
596 lockTable.emplace(
597 std::make_pair(transactionId, refLockRequestStructure));
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600598
599 // save the lock in the persistent file
600 saveLocks();
manojkiraneda0b631ae2019-12-03 17:54:28 +0530601 }
602 return std::make_pair(false, transactionId);
603}
604
Ratan Gupta07386c62019-12-14 14:06:09 +0530605inline bool Lock::isConflictRequest(const LockRequests refLockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530606{
607 // check for all the locks coming in as a part of single request
608 // return conflict if any two lock requests are conflicting
609
610 if (refLockRequestStructure.size() == 1)
611 {
612 BMCWEB_LOG_DEBUG << "Only single lock request, so there is no conflict";
613 // This means , we have only one lock request in the current
614 // request , so no conflict within the request
615 return false;
616 }
617
618 else
619 {
620 BMCWEB_LOG_DEBUG
621 << "There are multiple lock requests coming in a single request";
622
623 // There are multiple requests a part of one request
624
625 for (uint32_t i = 0; i < refLockRequestStructure.size(); i++)
626 {
627 for (uint32_t j = i + 1; j < refLockRequestStructure.size(); j++)
628 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500629 const LockRequest& p = refLockRequestStructure[i];
630 const LockRequest& q = refLockRequestStructure[j];
manojkiraneda0b631ae2019-12-03 17:54:28 +0530631 bool status = isConflictRecord(p, q);
632
633 if (status)
634 {
635 return true;
636 }
637 }
638 }
639 }
640 return false;
641}
642
643// This function converts the provided uint64_t resource id's from the two
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500644// lock requests subjected for comparison, and this function also compares
manojkiraneda0b631ae2019-12-03 17:54:28 +0530645// the content by bytes mentioned by a uint32_t number.
646
647// If all the elements in the lock requests which are subjected for comparison
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500648// are same, then the last comparison would be to check for the respective
manojkiraneda0b631ae2019-12-03 17:54:28 +0530649// bytes in the resourceid based on the segment length.
650
Ratan Gupta07386c62019-12-14 14:06:09 +0530651inline bool Lock::checkByte(uint64_t resourceId1, uint64_t resourceId2,
652 uint32_t position)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530653{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500654 uint8_t* p = reinterpret_cast<uint8_t*>(&resourceId1);
655 uint8_t* q = reinterpret_cast<uint8_t*>(&resourceId2);
manojkiraneda0b631ae2019-12-03 17:54:28 +0530656
657 BMCWEB_LOG_DEBUG << "Comparing bytes " << std::to_string(p[position]) << ","
658 << std::to_string(q[position]);
659 if (p[position] != q[position])
660 {
661 return false;
662 }
663
664 else
665 {
666 return true;
667 }
Ratan Gupta07386c62019-12-14 14:06:09 +0530668
manojkiraneda0b631ae2019-12-03 17:54:28 +0530669 return true;
670}
671
Ratan Gupta07386c62019-12-14 14:06:09 +0530672inline bool Lock::isConflictRecord(const LockRequest refLockRecord1,
673 const LockRequest refLockRecord2)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530674{
675 // No conflict if both are read locks
676
677 if (boost::equals(std::get<2>(refLockRecord1), "Read") &&
678 boost::equals(std::get<2>(refLockRecord2), "Read"))
679 {
680 BMCWEB_LOG_DEBUG << "Both are read locks, no conflict";
681 return false;
682 }
683
684 else
685 {
686 uint32_t i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500687 for (const auto& p : std::get<4>(refLockRecord1))
manojkiraneda0b631ae2019-12-03 17:54:28 +0530688 {
689
690 // return conflict when any of them is try to lock all resources
691 // under the current resource level.
692 if (boost::equals(p.first, "LockAll") ||
693 boost::equals(std::get<4>(refLockRecord2)[i].first, "LockAll"))
694 {
695 BMCWEB_LOG_DEBUG
696 << "Either of the Comparing locks are trying to lock all "
697 "resources under the current resource level";
698 return true;
699 }
700
701 // determine if there is a lock-all-with-same-segment-size.
702 // If the current segment sizes are the same,then we should fail.
703
704 if ((boost::equals(p.first, "LockSame") ||
705 boost::equals(std::get<4>(refLockRecord2)[i].first,
706 "LockSame")) &&
707 (p.second == std::get<4>(refLockRecord2)[i].second))
708 {
709 return true;
710 }
711
712 // if segment lengths are not the same, it means two different locks
713 // So no conflict
714 if (p.second != std::get<4>(refLockRecord2)[i].second)
715 {
716 BMCWEB_LOG_DEBUG << "Segment lengths are not same";
717 BMCWEB_LOG_DEBUG << "Segment 1 length : " << p.second;
718 BMCWEB_LOG_DEBUG << "Segment 2 length : "
719 << std::get<4>(refLockRecord2)[i].second;
720 return false;
721 }
722
723 // compare segment data
724
725 for (uint32_t i = 0; i < p.second; i++)
726 {
727 // if the segment data is different , then the locks is on a
Manojkiran Eda55fd1a92020-04-30 19:06:48 +0530728 // different resource So no conflict between the lock
729 // records.
730 // BMC is little endian , but the resourceID is formed by
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500731 // the Management Console in such a way that, the first byte
Manojkiran Eda55fd1a92020-04-30 19:06:48 +0530732 // from the MSB Position corresponds to the First Segment
733 // data. Therefore we need to convert the in-comming
734 // resourceID into Big Endian before processing further.
735 if (!(checkByte(boost::endian::endian_reverse(
736 std::get<3>(refLockRecord1)),
737 boost::endian::endian_reverse(
738 std::get<3>(refLockRecord2)),
739 i)))
manojkiraneda0b631ae2019-12-03 17:54:28 +0530740 {
741 return false;
742 }
743 }
744
745 ++i;
746 }
747 }
748
749 return false;
750}
751
Ratan Gupta07386c62019-12-14 14:06:09 +0530752inline uint32_t Lock::generateTransactionId()
manojkiraneda0b631ae2019-12-03 17:54:28 +0530753{
754 ++transactionId;
755 return transactionId;
756}
757
758} // namespace ibm_mc_lock
759} // namespace crow