blob: cb9191f25208a34f0261cb730ddc73a700acc409 [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
191 virtual ~Lock()
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500192 {}
Ratan Gupta07386c62019-12-14 14:06:09 +0530193};
manojkiraneda0b631ae2019-12-03 17:54:28 +0530194
Ratan Gupta07386c62019-12-14 14:06:09 +0530195inline bool Lock::createPersistentLockFilePath()
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600196{
197 // The path /var/lib/obmc will be created by initrdscripts
198 // Create the directories for the persistent lock file
199 std::error_code ec;
200 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt", ec))
201 {
202 std::filesystem::create_directory("/var/lib/obmc/bmc-console-mgmt", ec);
203 }
204 if (ec)
205 {
206 BMCWEB_LOG_DEBUG
207 << "Failed to prepare bmc-console-mgmt directory. ec : " << ec;
208 return false;
209 }
210
211 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt/locks",
212 ec))
213 {
214 std::filesystem::create_directory(
215 "/var/lib/obmc/bmc-console-mgmt/locks", ec);
216 }
217 if (ec)
218 {
219 BMCWEB_LOG_DEBUG
220 << "Failed to prepare persistent lock file directory. ec : " << ec;
221 return false;
222 }
223 return true;
224}
225
Ratan Gupta07386c62019-12-14 14:06:09 +0530226inline void Lock::loadLocks()
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600227{
228 std::ifstream persistentFile(fileName);
229 if (persistentFile.is_open())
230 {
231 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
232 if (data.is_discarded())
233 {
234 BMCWEB_LOG_ERROR << "Error parsing persistent data in json file.";
235 return;
236 }
237 BMCWEB_LOG_DEBUG << "The persistent lock data is available";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500238 for (const auto& item : data.items())
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600239 {
240 BMCWEB_LOG_DEBUG << item.key();
241 BMCWEB_LOG_DEBUG << item.value();
242 LockRequests locks = item.value();
Ratan Gupta07386c62019-12-14 14:06:09 +0530243 lockTable.emplace(std::pair<uint32_t, LockRequests>(
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600244 std::stoul(item.key()), locks));
245 BMCWEB_LOG_DEBUG << "The persistent lock data loaded";
246 }
247 }
248}
249
Ratan Gupta07386c62019-12-14 14:06:09 +0530250inline void Lock::saveLocks()
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600251{
252 std::error_code ec;
253 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt/locks",
254 ec))
255 {
256 if (!createPersistentLockFilePath())
257 {
258 BMCWEB_LOG_DEBUG << "Failed to create lock persistent path";
259 return;
260 }
261 }
262 std::ofstream persistentFile(fileName);
263 // set the permission of the file to 640
264 fs::perms permission =
265 fs::perms::owner_read | fs::perms::owner_write | fs::perms::group_read;
266 fs::permissions(fileName, permission);
267 nlohmann::json data;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500268 for (const auto& it : lockTable)
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600269 {
270 data[std::to_string(it.first)] = it.second;
271 }
272 BMCWEB_LOG_DEBUG << "data is " << data;
273 persistentFile << data;
274}
275
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500276inline RcGetLockList Lock::getLockList(const ListOfSessionIds& listSessionId)
manojkiraneda402b5712019-12-13 17:07:09 +0530277{
278
279 std::vector<std::pair<uint32_t, LockRequests>> lockList;
280
281 if (!lockTable.empty())
282 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500283 for (const auto& i : listSessionId)
manojkiraneda402b5712019-12-13 17:07:09 +0530284 {
285 auto it = lockTable.begin();
286 while (it != lockTable.end())
287 {
288 // Check if session id of this entry matches with session id
289 // given
290 if (std::get<0>(it->second[0]) == i)
291 {
292 BMCWEB_LOG_DEBUG << "Session id is found in the locktable";
293
294 // Push the whole lock record into a vector for returning
295 // the json
296 lockList.push_back(std::make_pair(it->first, it->second));
297 }
298 // Go to next entry in map
299 it++;
300 }
301 }
302 }
303 // we may have found at least one entry with the given session id
304 // return the json list of lock records pertaining to the given
305 // session id, or send an empty list if lock table is empty
306 return lockList;
307}
308
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500309inline RcReleaseLockApi Lock::releaseLock(const ListOfTransactionIds& p,
310 const SessionFlags& ids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530311{
312
313 bool status = validateRids(p);
314
315 if (!status)
316 {
317 // Validation of rids failed
318 BMCWEB_LOG_DEBUG << "Not a Valid request id";
319 return std::make_pair(false, status);
320 }
321 else
322 {
323 // Validation passed, check if all the locks are owned by the
324 // requesting HMC
325 auto status = isItMyLock(p, ids);
326 if (status.first)
327 {
328 // The current hmc owns all the locks, so we can release
329 // them
330 releaseLock(p);
331 }
332 return std::make_pair(true, status);
333 }
334 return std::make_pair(false, status);
335}
336
Ratan Gupta07386c62019-12-14 14:06:09 +0530337inline RcAcquireLock Lock::acquireLock(const LockRequests lockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530338{
339
340 // validate the lock request
341
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500342 for (auto& lockRecord : lockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530343 {
344 bool status = isValidLockRequest(lockRecord);
345 if (!status)
346 {
347 BMCWEB_LOG_DEBUG << "Not a Valid record";
348 BMCWEB_LOG_DEBUG << "Bad json in request";
349 return std::make_pair(true, std::make_pair(status, 0));
350 }
351 }
352 // check for conflict record
353
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500354 const LockRequests& multiRequest = lockRequestStructure;
manojkiraneda0b631ae2019-12-03 17:54:28 +0530355 bool status = isConflictRequest(multiRequest);
356
357 if (status)
358 {
359 BMCWEB_LOG_DEBUG << "There is a conflict within itself";
360 return std::make_pair(true, std::make_pair(status, 1));
361 }
362 else
363 {
364 BMCWEB_LOG_DEBUG << "The request is not conflicting within itself";
365
366 // Need to check for conflict with the locktable entries.
367
368 auto conflict = isConflictWithTable(multiRequest);
369
370 BMCWEB_LOG_DEBUG << "Done with checking conflict with the locktable";
371 return std::make_pair(false, conflict);
372 }
373
374 return std::make_pair(true, std::make_pair(true, 1));
375}
376
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500377inline void Lock::releaseLock(const ListOfTransactionIds& refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530378{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500379 for (const auto& id : refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530380 {
381 if (lockTable.erase(id))
382 {
383 BMCWEB_LOG_DEBUG << "Removing the locks with transaction ID : "
384 << id;
385 }
386
387 else
388 {
389 BMCWEB_LOG_DEBUG << "Removing the locks from the lock table "
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500390 "failed, transaction ID: "
manojkiraneda3b6dea62019-12-13 17:05:36 +0530391 << id;
392 }
393 }
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600394
395 saveLocks();
manojkiraneda3b6dea62019-12-13 17:05:36 +0530396}
397
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500398inline void Lock::releaseLock(const std::string& sessionId)
Ratan Gupta07386c62019-12-14 14:06:09 +0530399{
400 bool isErased = false;
401 if (!lockTable.empty())
402 {
403 auto it = lockTable.begin();
404 while (it != lockTable.end())
405 {
406 if (it->second.size() != 0)
407 {
408 // Check if session id of this entry matches with session id
409 // given
410 if (std::get<0>(it->second[0]) == sessionId)
411 {
412 BMCWEB_LOG_DEBUG << "Remove the lock from the locktable "
413 "having sessionID="
414 << sessionId;
415 BMCWEB_LOG_DEBUG << "TransactionID =" << it->first;
416 it = lockTable.erase(it);
417 isErased = true;
418 }
419 else
420 {
421 it++;
422 }
423 }
424 }
425 if (isErased)
426 {
427 // save the lock in the persistent file
428 saveLocks();
429 }
430 }
431}
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500432inline RcRelaseLock Lock::isItMyLock(const ListOfTransactionIds& refRids,
433 const SessionFlags& ids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530434{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500435 for (const auto& id : refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530436 {
437 // Just need to compare the client id of the first lock records in the
438 // complete lock row(in the map), because the rest of the lock records
439 // would have the same client id
440
441 std::string expectedClientId = std::get<1>(lockTable[id][0]);
442 std::string expectedSessionId = std::get<0>(lockTable[id][0]);
443
444 if ((expectedClientId == ids.first) &&
445 (expectedSessionId == ids.second))
446 {
447 // It is owned by the currently request hmc
448 BMCWEB_LOG_DEBUG << "Lock is owned by the current hmc";
449 }
450 else
451 {
452 BMCWEB_LOG_DEBUG << "Lock is not owned by the current hmc";
453 return std::make_pair(false, std::make_pair(id, lockTable[id][0]));
454 }
455 }
456 return std::make_pair(true, std::make_pair(0, LockRequest()));
457}
458
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500459inline bool Lock::validateRids(const ListOfTransactionIds& refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530460{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500461 for (const auto& id : refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530462 {
463 auto search = lockTable.find(id);
464
465 if (search != lockTable.end())
466 {
467 BMCWEB_LOG_DEBUG << "Valid Transaction id";
468 // continue for the next rid
469 }
470 else
471 {
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500472 BMCWEB_LOG_DEBUG << "At least 1 inValid Request id";
manojkiraneda3b6dea62019-12-13 17:05:36 +0530473 return false;
474 }
475 }
476 return true;
477}
478
Ratan Gupta07386c62019-12-14 14:06:09 +0530479inline bool Lock::isValidLockRequest(const LockRequest refLockRecord)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530480{
481
482 // validate the locktype
483
484 if (!((boost::equals(std::get<2>(refLockRecord), "Read") ||
485 (boost::equals(std::get<2>(refLockRecord), "Write")))))
486 {
487 BMCWEB_LOG_DEBUG << "Validation of LockType Failed";
488 BMCWEB_LOG_DEBUG << "Locktype : " << std::get<2>(refLockRecord);
489 return false;
490 }
491
492 BMCWEB_LOG_DEBUG << static_cast<int>(std::get<4>(refLockRecord).size());
493
494 // validate the number of segments
495 // Allowed No of segments are between 2 and 6
496 if ((static_cast<int>(std::get<4>(refLockRecord).size()) > 6) ||
497 (static_cast<int>(std::get<4>(refLockRecord).size()) < 2))
498 {
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500499 BMCWEB_LOG_DEBUG << "Validation of Number of Segments Failed";
manojkiraneda0b631ae2019-12-03 17:54:28 +0530500 BMCWEB_LOG_DEBUG << "Number of Segments provied : "
501 << sizeof(std::get<4>(refLockRecord));
502 return false;
503 }
504
505 int lockFlag = 0;
506 // validate the lockflags & segment length
507
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500508 for (const auto& p : std::get<4>(refLockRecord))
manojkiraneda0b631ae2019-12-03 17:54:28 +0530509 {
510
511 // validate the lock flags
512 // Allowed lockflags are locksame,lockall & dontlock
513
514 if (!((boost::equals(p.first, "LockSame") ||
515 (boost::equals(p.first, "LockAll")) ||
516 (boost::equals(p.first, "DontLock")))))
517 {
518 BMCWEB_LOG_DEBUG << "Validation of lock flags failed";
519 BMCWEB_LOG_DEBUG << p.first;
520 return false;
521 }
522
523 // validate the segment length
524 // Allowed values of segment length are between 1 and 4
525
526 if (p.second < 1 || p.second > 4)
527 {
528 BMCWEB_LOG_DEBUG << "Validation of Segment Length Failed";
529 BMCWEB_LOG_DEBUG << p.second;
530 return false;
531 }
532
533 if ((boost::equals(p.first, "LockSame") ||
534 (boost::equals(p.first, "LockAll"))))
535 {
536 ++lockFlag;
537 if (lockFlag >= 2)
538 {
539 return false;
540 }
541 }
542 }
543
manojkiraneda0b631ae2019-12-03 17:54:28 +0530544 return true;
545}
546
Ratan Gupta07386c62019-12-14 14:06:09 +0530547inline Rc Lock::isConflictWithTable(const LockRequests refLockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530548{
549
550 uint32_t transactionId;
551
552 if (lockTable.empty())
553 {
554 transactionId = generateTransactionId();
555 BMCWEB_LOG_DEBUG << transactionId;
556 // Lock table is empty, so we are safe to add the lockrecords
557 // as there will be no conflict
558 BMCWEB_LOG_DEBUG << "Lock table is empty, so adding the lockrecords";
559 lockTable.emplace(std::pair<uint32_t, LockRequests>(
560 transactionId, refLockRequestStructure));
561
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600562 // save the lock in the persistent file
563 saveLocks();
manojkiraneda0b631ae2019-12-03 17:54:28 +0530564 return std::make_pair(false, transactionId);
565 }
566
567 else
568 {
569 BMCWEB_LOG_DEBUG
570 << "Lock table is not empty, check for conflict with lock table";
571 // Lock table is not empty, compare the lockrequest entries with
572 // the entries in the lock table
573
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500574 for (const auto& lockRecord1 : refLockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530575 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500576 for (const auto& map : lockTable)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530577 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500578 for (const auto& lockRecord2 : map.second)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530579 {
580 bool status = isConflictRecord(lockRecord1, lockRecord2);
581 if (status)
582 {
583 return std::make_pair(
584 true, std::make_pair(map.first, lockRecord2));
585 }
586 }
587 }
588 }
589
590 // Reached here, so no conflict with the locktable, so we are safe to
591 // add the request records into the lock table
592
593 // Lock table is empty, so we are safe to add the lockrecords
594 // as there will be no conflict
595 BMCWEB_LOG_DEBUG << " Adding elements into lock table";
596 transactionId = generateTransactionId();
597 lockTable.emplace(
598 std::make_pair(transactionId, refLockRequestStructure));
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600599
600 // save the lock in the persistent file
601 saveLocks();
manojkiraneda0b631ae2019-12-03 17:54:28 +0530602 }
603 return std::make_pair(false, transactionId);
604}
605
Ratan Gupta07386c62019-12-14 14:06:09 +0530606inline bool Lock::isConflictRequest(const LockRequests refLockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530607{
608 // check for all the locks coming in as a part of single request
609 // return conflict if any two lock requests are conflicting
610
611 if (refLockRequestStructure.size() == 1)
612 {
613 BMCWEB_LOG_DEBUG << "Only single lock request, so there is no conflict";
614 // This means , we have only one lock request in the current
615 // request , so no conflict within the request
616 return false;
617 }
618
619 else
620 {
621 BMCWEB_LOG_DEBUG
622 << "There are multiple lock requests coming in a single request";
623
624 // There are multiple requests a part of one request
625
626 for (uint32_t i = 0; i < refLockRequestStructure.size(); i++)
627 {
628 for (uint32_t j = i + 1; j < refLockRequestStructure.size(); j++)
629 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500630 const LockRequest& p = refLockRequestStructure[i];
631 const LockRequest& q = refLockRequestStructure[j];
manojkiraneda0b631ae2019-12-03 17:54:28 +0530632 bool status = isConflictRecord(p, q);
633
634 if (status)
635 {
636 return true;
637 }
638 }
639 }
640 }
641 return false;
642}
643
644// This function converts the provided uint64_t resource id's from the two
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500645// lock requests subjected for comparison, and this function also compares
manojkiraneda0b631ae2019-12-03 17:54:28 +0530646// the content by bytes mentioned by a uint32_t number.
647
648// If all the elements in the lock requests which are subjected for comparison
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500649// are same, then the last comparison would be to check for the respective
manojkiraneda0b631ae2019-12-03 17:54:28 +0530650// bytes in the resourceid based on the segment length.
651
Ratan Gupta07386c62019-12-14 14:06:09 +0530652inline bool Lock::checkByte(uint64_t resourceId1, uint64_t resourceId2,
653 uint32_t position)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530654{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500655 uint8_t* p = reinterpret_cast<uint8_t*>(&resourceId1);
656 uint8_t* q = reinterpret_cast<uint8_t*>(&resourceId2);
manojkiraneda0b631ae2019-12-03 17:54:28 +0530657
658 BMCWEB_LOG_DEBUG << "Comparing bytes " << std::to_string(p[position]) << ","
659 << std::to_string(q[position]);
660 if (p[position] != q[position])
661 {
662 return false;
663 }
664
665 else
666 {
667 return true;
668 }
Ratan Gupta07386c62019-12-14 14:06:09 +0530669
manojkiraneda0b631ae2019-12-03 17:54:28 +0530670 return true;
671}
672
Ratan Gupta07386c62019-12-14 14:06:09 +0530673inline bool Lock::isConflictRecord(const LockRequest refLockRecord1,
674 const LockRequest refLockRecord2)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530675{
676 // No conflict if both are read locks
677
678 if (boost::equals(std::get<2>(refLockRecord1), "Read") &&
679 boost::equals(std::get<2>(refLockRecord2), "Read"))
680 {
681 BMCWEB_LOG_DEBUG << "Both are read locks, no conflict";
682 return false;
683 }
684
685 else
686 {
687 uint32_t i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500688 for (const auto& p : std::get<4>(refLockRecord1))
manojkiraneda0b631ae2019-12-03 17:54:28 +0530689 {
690
691 // return conflict when any of them is try to lock all resources
692 // under the current resource level.
693 if (boost::equals(p.first, "LockAll") ||
694 boost::equals(std::get<4>(refLockRecord2)[i].first, "LockAll"))
695 {
696 BMCWEB_LOG_DEBUG
697 << "Either of the Comparing locks are trying to lock all "
698 "resources under the current resource level";
699 return true;
700 }
701
702 // determine if there is a lock-all-with-same-segment-size.
703 // If the current segment sizes are the same,then we should fail.
704
705 if ((boost::equals(p.first, "LockSame") ||
706 boost::equals(std::get<4>(refLockRecord2)[i].first,
707 "LockSame")) &&
708 (p.second == std::get<4>(refLockRecord2)[i].second))
709 {
710 return true;
711 }
712
713 // if segment lengths are not the same, it means two different locks
714 // So no conflict
715 if (p.second != std::get<4>(refLockRecord2)[i].second)
716 {
717 BMCWEB_LOG_DEBUG << "Segment lengths are not same";
718 BMCWEB_LOG_DEBUG << "Segment 1 length : " << p.second;
719 BMCWEB_LOG_DEBUG << "Segment 2 length : "
720 << std::get<4>(refLockRecord2)[i].second;
721 return false;
722 }
723
724 // compare segment data
725
726 for (uint32_t i = 0; i < p.second; i++)
727 {
728 // if the segment data is different , then the locks is on a
Manojkiran Eda55fd1a92020-04-30 19:06:48 +0530729 // different resource So no conflict between the lock
730 // records.
731 // BMC is little endian , but the resourceID is formed by
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500732 // the Management Console in such a way that, the first byte
Manojkiran Eda55fd1a92020-04-30 19:06:48 +0530733 // from the MSB Position corresponds to the First Segment
734 // data. Therefore we need to convert the in-comming
735 // resourceID into Big Endian before processing further.
736 if (!(checkByte(boost::endian::endian_reverse(
737 std::get<3>(refLockRecord1)),
738 boost::endian::endian_reverse(
739 std::get<3>(refLockRecord2)),
740 i)))
manojkiraneda0b631ae2019-12-03 17:54:28 +0530741 {
742 return false;
743 }
744 }
745
746 ++i;
747 }
748 }
749
750 return false;
751}
752
Ratan Gupta07386c62019-12-14 14:06:09 +0530753inline uint32_t Lock::generateTransactionId()
manojkiraneda0b631ae2019-12-03 17:54:28 +0530754{
755 ++transactionId;
756 return transactionId;
757}
758
759} // namespace ibm_mc_lock
760} // namespace crow