blob: 201085f0bfcbd7d4b376cfbc8b0360ad5df6c3e0 [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
manojkiraneda0b631ae2019-12-03 17:54:28 +053018using SType = std::string;
19
20/*----------------------------------------
21|Segment flags : LockFlag | SegmentLength|
22------------------------------------------*/
23
24using SegmentFlags = std::vector<std::pair<SType, uint32_t>>;
25
26// Lockrequest = session-id | hmc-id | locktype | resourceid | segmentinfo
27using LockRequest = std::tuple<SType, SType, SType, uint64_t, SegmentFlags>;
manojkiraneda0b631ae2019-12-03 17:54:28 +053028using LockRequests = std::vector<LockRequest>;
29using Rc =
30 std::pair<bool, std::variant<uint32_t, std::pair<uint32_t, LockRequest>>>;
manojkiraneda3b6dea62019-12-13 17:05:36 +053031using RcRelaseLock = std::pair<bool, std::pair<uint32_t, LockRequest>>;
manojkiraneda402b5712019-12-13 17:07:09 +053032using RcGetLockList =
33 std::variant<std::string, std::vector<std::pair<uint32_t, LockRequests>>>;
manojkiraneda3b6dea62019-12-13 17:05:36 +053034using ListOfTransactionIds = std::vector<uint32_t>;
manojkiraneda0b631ae2019-12-03 17:54:28 +053035using RcAcquireLock = std::pair<bool, std::variant<Rc, std::pair<bool, int>>>;
manojkiraneda3b6dea62019-12-13 17:05:36 +053036using RcReleaseLockApi = std::pair<bool, std::variant<bool, RcRelaseLock>>;
37using SessionFlags = std::pair<SType, SType>;
manojkiraneda402b5712019-12-13 17:07:09 +053038using ListOfSessionIds = std::vector<std::string>;
Gunnar Mills1214b7e2020-06-04 10:11:30 -050039static constexpr const char* fileName =
Sunitha Harish8a3bb712019-12-13 03:48:09 -060040 "/var/lib/obmc/bmc-console-mgmt/locks/ibm_mc_persistent_lock_data.json";
manojkiraneda0b631ae2019-12-03 17:54:28 +053041
42class Lock
43{
44 uint32_t transactionId;
45 boost::container::flat_map<uint32_t, LockRequests> lockTable;
46
47 /*
Sunitha Harish8a3bb712019-12-13 03:48:09 -060048 * This API implements the logic to persist the locks that are contained in
49 * the lock table into a json file.
50 */
51 void saveLocks();
52
53 /*
54 * This API implements the logic to load the locks that are present in the
55 * json file into the lock table.
56 */
57 void loadLocks();
58
manojkiraneda4eaf2ee2019-12-13 17:10:41 +053059 bool createPersistentLockFilePath();
60
61 protected:
62 /*
63 * This function implements the logic for validating an incoming
64 * lock request/requests.
65 *
66 * Returns : True (if Valid)
67 * Returns : False (if not a Valid lock request)
68 */
69
Ed Tanousb5a76932020-09-29 16:16:58 -070070 virtual bool isValidLockRequest(const LockRequest&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +053071
72 /*
73 * This function implements the logic of checking if the incoming
74 * multi-lock request is not having conflicting requirements.
75 *
76 * Returns : True (if conflicting)
77 * Returns : False (if not conflicting)
78 */
79
Ed Tanousb5a76932020-09-29 16:16:58 -070080 virtual bool isConflictRequest(const LockRequests&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +053081 /*
82 * Implements the core algorithm to find the conflicting
83 * lock requests.
84 *
85 * This functions takes two lock requests and check if both
86 * are conflicting to each other.
87 *
88 * Returns : True (if conflicting)
89 * Returns : False (if not conflicting)
90 */
Ed Tanousb5a76932020-09-29 16:16:58 -070091 virtual bool isConflictRecord(const LockRequest&, const LockRequest&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +053092
93 /*
94 * This function implements the logic of checking the conflicting
95 * locks from a incoming single/multi lock requests with the already
96 * existing lock request in the lock table.
97 *
98 */
99
Ed Tanousb5a76932020-09-29 16:16:58 -0700100 virtual Rc isConflictWithTable(const LockRequests&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530101 /*
102 * This function implements the logic of checking the ownership of the
103 * lock from the releaselock request.
104 *
105 * Returns : True (if the requesting HMC & Session owns the lock(s))
106 * Returns : False (if the request HMC or Session does not own the lock(s))
107 */
108
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500109 virtual RcRelaseLock isItMyLock(const ListOfTransactionIds&,
110 const SessionFlags&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530111
112 /*
113 * This function validates the the list of transactionID's and returns false
114 * if the transaction ID is not valid & not present in the lock table
115 */
116
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500117 virtual bool validateRids(const ListOfTransactionIds&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530118
119 /*
120 * This function releases the locks that are already obtained by the
121 * requesting Management console.
122 */
123
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500124 void releaseLock(const ListOfTransactionIds&);
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530125
126 Lock()
127 {
128 loadLocks();
129 transactionId = lockTable.empty() ? 0 : prev(lockTable.end())->first;
130 }
131
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600132 /*
manojkiraneda0b631ae2019-12-03 17:54:28 +0530133 * This function implements the algorithm for checking the respective
134 * bytes of the resource id based on the lock management algorithm.
135 */
136
137 bool checkByte(uint64_t, uint64_t, uint32_t);
138
139 /*
140 * This functions implements a counter that generates a unique 32 bit
141 * number for every successful transaction. This number will be used by
142 * the Management Console for debug.
143 */
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530144 virtual uint32_t generateTransactionId();
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600145
manojkiraneda0b631ae2019-12-03 17:54:28 +0530146 public:
147 /*
148 * This function implements the logic for acquiring a lock on a
Manojkiran Eda5bb0ece2020-01-20 20:22:36 +0530149 * resource if the incoming request is legitimate without any
manojkiraneda0b631ae2019-12-03 17:54:28 +0530150 * conflicting requirements & without any conflicting requirement
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500151 * with the existing locks in the lock table.
manojkiraneda0b631ae2019-12-03 17:54:28 +0530152 *
153 */
154
Ed Tanousb5a76932020-09-29 16:16:58 -0700155 RcAcquireLock acquireLock(const LockRequests&);
manojkiraneda0b631ae2019-12-03 17:54:28 +0530156
manojkiraneda3b6dea62019-12-13 17:05:36 +0530157 /*
158 * This function implements the logic for releasing the lock that are
159 * owned by a management console session.
160 *
161 * The locks can be released by two ways
162 * - Using list of transaction ID's
163 * - Using a Session ID
164 *
165 * Client can choose either of the ways by using `Type` JSON key.
166 *
167 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500168 RcReleaseLockApi releaseLock(const ListOfTransactionIds&,
169 const SessionFlags&);
manojkiraneda3b6dea62019-12-13 17:05:36 +0530170
manojkiraneda402b5712019-12-13 17:07:09 +0530171 /*
172 * This function implements the logic for getting the list of locks obtained
173 * by a particular management console.
174 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500175 RcGetLockList getLockList(const ListOfSessionIds&);
manojkiraneda402b5712019-12-13 17:07:09 +0530176
Ratan Gupta07386c62019-12-14 14:06:09 +0530177 /*
178 * This function is releases all the locks obtained by a particular
179 * session.
180 */
181
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500182 void releaseLock(const std::string&);
Ratan Gupta07386c62019-12-14 14:06:09 +0530183
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500184 static Lock& getInstance()
Ratan Gupta07386c62019-12-14 14:06:09 +0530185 {
186 static Lock lockObject;
187 return lockObject;
188 }
manojkiraneda4eaf2ee2019-12-13 17:10:41 +0530189
Ed Tanous4e087512020-09-28 18:41:25 -0700190 virtual ~Lock() = default;
Ratan Gupta07386c62019-12-14 14:06:09 +0530191};
manojkiraneda0b631ae2019-12-03 17:54:28 +0530192
Ratan Gupta07386c62019-12-14 14:06:09 +0530193inline bool Lock::createPersistentLockFilePath()
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600194{
195 // The path /var/lib/obmc will be created by initrdscripts
196 // Create the directories for the persistent lock file
197 std::error_code ec;
198 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt", ec))
199 {
200 std::filesystem::create_directory("/var/lib/obmc/bmc-console-mgmt", ec);
201 }
202 if (ec)
203 {
204 BMCWEB_LOG_DEBUG
205 << "Failed to prepare bmc-console-mgmt directory. ec : " << ec;
206 return false;
207 }
208
209 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt/locks",
210 ec))
211 {
212 std::filesystem::create_directory(
213 "/var/lib/obmc/bmc-console-mgmt/locks", ec);
214 }
215 if (ec)
216 {
217 BMCWEB_LOG_DEBUG
218 << "Failed to prepare persistent lock file directory. ec : " << ec;
219 return false;
220 }
221 return true;
222}
223
Ratan Gupta07386c62019-12-14 14:06:09 +0530224inline void Lock::loadLocks()
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600225{
226 std::ifstream persistentFile(fileName);
227 if (persistentFile.is_open())
228 {
229 auto data = nlohmann::json::parse(persistentFile, nullptr, false);
230 if (data.is_discarded())
231 {
232 BMCWEB_LOG_ERROR << "Error parsing persistent data in json file.";
233 return;
234 }
235 BMCWEB_LOG_DEBUG << "The persistent lock data is available";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500236 for (const auto& item : data.items())
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600237 {
238 BMCWEB_LOG_DEBUG << item.key();
239 BMCWEB_LOG_DEBUG << item.value();
240 LockRequests locks = item.value();
Ratan Gupta07386c62019-12-14 14:06:09 +0530241 lockTable.emplace(std::pair<uint32_t, LockRequests>(
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600242 std::stoul(item.key()), locks));
243 BMCWEB_LOG_DEBUG << "The persistent lock data loaded";
244 }
245 }
246}
247
Ratan Gupta07386c62019-12-14 14:06:09 +0530248inline void Lock::saveLocks()
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600249{
250 std::error_code ec;
251 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt/locks",
252 ec))
253 {
254 if (!createPersistentLockFilePath())
255 {
256 BMCWEB_LOG_DEBUG << "Failed to create lock persistent path";
257 return;
258 }
259 }
260 std::ofstream persistentFile(fileName);
261 // set the permission of the file to 640
Ed Tanousb5a76932020-09-29 16:16:58 -0700262 std::filesystem::perms permission = std::filesystem::perms::owner_read |
263 std::filesystem::perms::owner_write |
264 std::filesystem::perms::group_read;
265 std::filesystem::permissions(fileName, permission);
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600266 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 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700320 // Validation passed, check if all the locks are owned by the
321 // requesting HMC
322 auto status2 = isItMyLock(p, ids);
323 if (status2.first)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530324 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700325 // The current hmc owns all the locks, so we can release
326 // them
327 releaseLock(p);
manojkiraneda3b6dea62019-12-13 17:05:36 +0530328 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700329 return std::make_pair(true, status);
manojkiraneda3b6dea62019-12-13 17:05:36 +0530330}
331
Ed Tanousb5a76932020-09-29 16:16:58 -0700332inline RcAcquireLock Lock::acquireLock(const LockRequests& lockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530333{
334
335 // validate the lock request
336
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500337 for (auto& lockRecord : lockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530338 {
339 bool status = isValidLockRequest(lockRecord);
340 if (!status)
341 {
342 BMCWEB_LOG_DEBUG << "Not a Valid record";
343 BMCWEB_LOG_DEBUG << "Bad json in request";
344 return std::make_pair(true, std::make_pair(status, 0));
345 }
346 }
347 // check for conflict record
348
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500349 const LockRequests& multiRequest = lockRequestStructure;
manojkiraneda0b631ae2019-12-03 17:54:28 +0530350 bool status = isConflictRequest(multiRequest);
351
352 if (status)
353 {
354 BMCWEB_LOG_DEBUG << "There is a conflict within itself";
355 return std::make_pair(true, std::make_pair(status, 1));
356 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700357 BMCWEB_LOG_DEBUG << "The request is not conflicting within itself";
manojkiraneda0b631ae2019-12-03 17:54:28 +0530358
Ed Tanous3174e4d2020-10-07 11:41:22 -0700359 // Need to check for conflict with the locktable entries.
manojkiraneda0b631ae2019-12-03 17:54:28 +0530360
Ed Tanous3174e4d2020-10-07 11:41:22 -0700361 auto conflict = isConflictWithTable(multiRequest);
manojkiraneda0b631ae2019-12-03 17:54:28 +0530362
Ed Tanous3174e4d2020-10-07 11:41:22 -0700363 BMCWEB_LOG_DEBUG << "Done with checking conflict with the locktable";
364 return std::make_pair(false, conflict);
manojkiraneda0b631ae2019-12-03 17:54:28 +0530365}
366
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500367inline void Lock::releaseLock(const ListOfTransactionIds& refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530368{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500369 for (const auto& id : refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530370 {
371 if (lockTable.erase(id))
372 {
373 BMCWEB_LOG_DEBUG << "Removing the locks with transaction ID : "
374 << id;
375 }
376
377 else
378 {
379 BMCWEB_LOG_DEBUG << "Removing the locks from the lock table "
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500380 "failed, transaction ID: "
manojkiraneda3b6dea62019-12-13 17:05:36 +0530381 << id;
382 }
383 }
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600384
385 saveLocks();
manojkiraneda3b6dea62019-12-13 17:05:36 +0530386}
387
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500388inline void Lock::releaseLock(const std::string& sessionId)
Ratan Gupta07386c62019-12-14 14:06:09 +0530389{
390 bool isErased = false;
391 if (!lockTable.empty())
392 {
393 auto it = lockTable.begin();
394 while (it != lockTable.end())
395 {
396 if (it->second.size() != 0)
397 {
398 // Check if session id of this entry matches with session id
399 // given
400 if (std::get<0>(it->second[0]) == sessionId)
401 {
402 BMCWEB_LOG_DEBUG << "Remove the lock from the locktable "
403 "having sessionID="
404 << sessionId;
405 BMCWEB_LOG_DEBUG << "TransactionID =" << it->first;
406 it = lockTable.erase(it);
407 isErased = true;
408 }
409 else
410 {
411 it++;
412 }
413 }
414 }
415 if (isErased)
416 {
417 // save the lock in the persistent file
418 saveLocks();
419 }
420 }
421}
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500422inline RcRelaseLock Lock::isItMyLock(const ListOfTransactionIds& refRids,
423 const SessionFlags& ids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530424{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500425 for (const auto& id : refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530426 {
427 // Just need to compare the client id of the first lock records in the
428 // complete lock row(in the map), because the rest of the lock records
429 // would have the same client id
430
431 std::string expectedClientId = std::get<1>(lockTable[id][0]);
432 std::string expectedSessionId = std::get<0>(lockTable[id][0]);
433
434 if ((expectedClientId == ids.first) &&
435 (expectedSessionId == ids.second))
436 {
437 // It is owned by the currently request hmc
438 BMCWEB_LOG_DEBUG << "Lock is owned by the current hmc";
439 }
440 else
441 {
442 BMCWEB_LOG_DEBUG << "Lock is not owned by the current hmc";
443 return std::make_pair(false, std::make_pair(id, lockTable[id][0]));
444 }
445 }
446 return std::make_pair(true, std::make_pair(0, LockRequest()));
447}
448
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500449inline bool Lock::validateRids(const ListOfTransactionIds& refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530450{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500451 for (const auto& id : refRids)
manojkiraneda3b6dea62019-12-13 17:05:36 +0530452 {
453 auto search = lockTable.find(id);
454
455 if (search != lockTable.end())
456 {
457 BMCWEB_LOG_DEBUG << "Valid Transaction id";
458 // continue for the next rid
459 }
460 else
461 {
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500462 BMCWEB_LOG_DEBUG << "At least 1 inValid Request id";
manojkiraneda3b6dea62019-12-13 17:05:36 +0530463 return false;
464 }
465 }
466 return true;
467}
468
Ed Tanousb5a76932020-09-29 16:16:58 -0700469inline bool Lock::isValidLockRequest(const LockRequest& refLockRecord)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530470{
471
472 // validate the locktype
473
474 if (!((boost::equals(std::get<2>(refLockRecord), "Read") ||
475 (boost::equals(std::get<2>(refLockRecord), "Write")))))
476 {
477 BMCWEB_LOG_DEBUG << "Validation of LockType Failed";
478 BMCWEB_LOG_DEBUG << "Locktype : " << std::get<2>(refLockRecord);
479 return false;
480 }
481
482 BMCWEB_LOG_DEBUG << static_cast<int>(std::get<4>(refLockRecord).size());
483
484 // validate the number of segments
485 // Allowed No of segments are between 2 and 6
486 if ((static_cast<int>(std::get<4>(refLockRecord).size()) > 6) ||
487 (static_cast<int>(std::get<4>(refLockRecord).size()) < 2))
488 {
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500489 BMCWEB_LOG_DEBUG << "Validation of Number of Segments Failed";
manojkiraneda0b631ae2019-12-03 17:54:28 +0530490 BMCWEB_LOG_DEBUG << "Number of Segments provied : "
Ed Tanous7cd94e42020-09-29 16:03:02 -0700491 << std::get<4>(refLockRecord).size();
manojkiraneda0b631ae2019-12-03 17:54:28 +0530492 return false;
493 }
494
495 int lockFlag = 0;
496 // validate the lockflags & segment length
497
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500498 for (const auto& p : std::get<4>(refLockRecord))
manojkiraneda0b631ae2019-12-03 17:54:28 +0530499 {
500
501 // validate the lock flags
502 // Allowed lockflags are locksame,lockall & dontlock
503
504 if (!((boost::equals(p.first, "LockSame") ||
505 (boost::equals(p.first, "LockAll")) ||
506 (boost::equals(p.first, "DontLock")))))
507 {
508 BMCWEB_LOG_DEBUG << "Validation of lock flags failed";
509 BMCWEB_LOG_DEBUG << p.first;
510 return false;
511 }
512
513 // validate the segment length
514 // Allowed values of segment length are between 1 and 4
515
516 if (p.second < 1 || p.second > 4)
517 {
518 BMCWEB_LOG_DEBUG << "Validation of Segment Length Failed";
519 BMCWEB_LOG_DEBUG << p.second;
520 return false;
521 }
522
523 if ((boost::equals(p.first, "LockSame") ||
524 (boost::equals(p.first, "LockAll"))))
525 {
526 ++lockFlag;
527 if (lockFlag >= 2)
528 {
529 return false;
530 }
531 }
532 }
533
manojkiraneda0b631ae2019-12-03 17:54:28 +0530534 return true;
535}
536
Ed Tanousb5a76932020-09-29 16:16:58 -0700537inline Rc Lock::isConflictWithTable(const LockRequests& refLockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530538{
539
540 uint32_t transactionId;
541
542 if (lockTable.empty())
543 {
544 transactionId = generateTransactionId();
545 BMCWEB_LOG_DEBUG << transactionId;
546 // Lock table is empty, so we are safe to add the lockrecords
547 // as there will be no conflict
548 BMCWEB_LOG_DEBUG << "Lock table is empty, so adding the lockrecords";
549 lockTable.emplace(std::pair<uint32_t, LockRequests>(
550 transactionId, refLockRequestStructure));
551
Sunitha Harish8a3bb712019-12-13 03:48:09 -0600552 // save the lock in the persistent file
553 saveLocks();
manojkiraneda0b631ae2019-12-03 17:54:28 +0530554 return std::make_pair(false, transactionId);
555 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700556 BMCWEB_LOG_DEBUG
557 << "Lock table is not empty, check for conflict with lock table";
558 // Lock table is not empty, compare the lockrequest entries with
559 // the entries in the lock table
manojkiraneda0b631ae2019-12-03 17:54:28 +0530560
Ed Tanous3174e4d2020-10-07 11:41:22 -0700561 for (const auto& lockRecord1 : refLockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530562 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700563 for (const auto& map : lockTable)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530564 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700565 for (const auto& lockRecord2 : map.second)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530566 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700567 bool status = isConflictRecord(lockRecord1, lockRecord2);
568 if (status)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530569 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700570 return std::make_pair(
571 true, std::make_pair(map.first, lockRecord2));
manojkiraneda0b631ae2019-12-03 17:54:28 +0530572 }
573 }
574 }
manojkiraneda0b631ae2019-12-03 17:54:28 +0530575 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700576
577 // Reached here, so no conflict with the locktable, so we are safe to
578 // add the request records into the lock table
579
580 // Lock table is empty, so we are safe to add the lockrecords
581 // as there will be no conflict
582 BMCWEB_LOG_DEBUG << " Adding elements into lock table";
583 transactionId = generateTransactionId();
584 lockTable.emplace(std::make_pair(transactionId, refLockRequestStructure));
585
586 // save the lock in the persistent file
587 saveLocks();
manojkiraneda0b631ae2019-12-03 17:54:28 +0530588 return std::make_pair(false, transactionId);
589}
590
Ed Tanousb5a76932020-09-29 16:16:58 -0700591inline bool Lock::isConflictRequest(const LockRequests& refLockRequestStructure)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530592{
593 // check for all the locks coming in as a part of single request
594 // return conflict if any two lock requests are conflicting
595
596 if (refLockRequestStructure.size() == 1)
597 {
598 BMCWEB_LOG_DEBUG << "Only single lock request, so there is no conflict";
599 // This means , we have only one lock request in the current
600 // request , so no conflict within the request
601 return false;
602 }
603
Ed Tanous3174e4d2020-10-07 11:41:22 -0700604 BMCWEB_LOG_DEBUG
605 << "There are multiple lock requests coming in a single request";
606
607 // There are multiple requests a part of one request
608
609 for (uint32_t i = 0; i < refLockRequestStructure.size(); i++)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530610 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700611 for (uint32_t j = i + 1; j < refLockRequestStructure.size(); j++)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530612 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700613 const LockRequest& p = refLockRequestStructure[i];
614 const LockRequest& q = refLockRequestStructure[j];
615 bool status = isConflictRecord(p, q);
manojkiraneda0b631ae2019-12-03 17:54:28 +0530616
Ed Tanous3174e4d2020-10-07 11:41:22 -0700617 if (status)
618 {
619 return true;
manojkiraneda0b631ae2019-12-03 17:54:28 +0530620 }
621 }
622 }
623 return false;
624}
625
626// This function converts the provided uint64_t resource id's from the two
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500627// lock requests subjected for comparison, and this function also compares
manojkiraneda0b631ae2019-12-03 17:54:28 +0530628// the content by bytes mentioned by a uint32_t number.
629
630// If all the elements in the lock requests which are subjected for comparison
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500631// are same, then the last comparison would be to check for the respective
manojkiraneda0b631ae2019-12-03 17:54:28 +0530632// bytes in the resourceid based on the segment length.
633
Ratan Gupta07386c62019-12-14 14:06:09 +0530634inline bool Lock::checkByte(uint64_t resourceId1, uint64_t resourceId2,
635 uint32_t position)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530636{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500637 uint8_t* p = reinterpret_cast<uint8_t*>(&resourceId1);
638 uint8_t* q = reinterpret_cast<uint8_t*>(&resourceId2);
manojkiraneda0b631ae2019-12-03 17:54:28 +0530639
640 BMCWEB_LOG_DEBUG << "Comparing bytes " << std::to_string(p[position]) << ","
641 << std::to_string(q[position]);
642 if (p[position] != q[position])
643 {
644 return false;
645 }
646
manojkiraneda0b631ae2019-12-03 17:54:28 +0530647 return true;
648}
649
Ed Tanousb5a76932020-09-29 16:16:58 -0700650inline bool Lock::isConflictRecord(const LockRequest& refLockRecord1,
651 const LockRequest& refLockRecord2)
manojkiraneda0b631ae2019-12-03 17:54:28 +0530652{
653 // No conflict if both are read locks
654
655 if (boost::equals(std::get<2>(refLockRecord1), "Read") &&
656 boost::equals(std::get<2>(refLockRecord2), "Read"))
657 {
658 BMCWEB_LOG_DEBUG << "Both are read locks, no conflict";
659 return false;
660 }
661
Ed Tanous3174e4d2020-10-07 11:41:22 -0700662 uint32_t i = 0;
663 for (const auto& p : std::get<4>(refLockRecord1))
manojkiraneda0b631ae2019-12-03 17:54:28 +0530664 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700665
666 // return conflict when any of them is try to lock all resources
667 // under the current resource level.
668 if (boost::equals(p.first, "LockAll") ||
669 boost::equals(std::get<4>(refLockRecord2)[i].first, "LockAll"))
manojkiraneda0b631ae2019-12-03 17:54:28 +0530670 {
Ed Tanous3174e4d2020-10-07 11:41:22 -0700671 BMCWEB_LOG_DEBUG
672 << "Either of the Comparing locks are trying to lock all "
673 "resources under the current resource level";
674 return true;
675 }
manojkiraneda0b631ae2019-12-03 17:54:28 +0530676
Ed Tanous3174e4d2020-10-07 11:41:22 -0700677 // determine if there is a lock-all-with-same-segment-size.
678 // If the current segment sizes are the same,then we should fail.
679
680 if ((boost::equals(p.first, "LockSame") ||
681 boost::equals(std::get<4>(refLockRecord2)[i].first, "LockSame")) &&
682 (p.second == std::get<4>(refLockRecord2)[i].second))
683 {
684 return true;
685 }
686
687 // if segment lengths are not the same, it means two different locks
688 // So no conflict
689 if (p.second != std::get<4>(refLockRecord2)[i].second)
690 {
691 BMCWEB_LOG_DEBUG << "Segment lengths are not same";
692 BMCWEB_LOG_DEBUG << "Segment 1 length : " << p.second;
693 BMCWEB_LOG_DEBUG << "Segment 2 length : "
694 << std::get<4>(refLockRecord2)[i].second;
695 return false;
696 }
697
698 // compare segment data
699
700 for (uint32_t i = 0; i < p.second; i++)
701 {
702 // if the segment data is different , then the locks is on a
703 // different resource So no conflict between the lock
704 // records.
705 // BMC is little endian , but the resourceID is formed by
706 // the Management Console in such a way that, the first byte
707 // from the MSB Position corresponds to the First Segment
708 // data. Therefore we need to convert the in-comming
709 // resourceID into Big Endian before processing further.
710 if (!(checkByte(
711 boost::endian::endian_reverse(std::get<3>(refLockRecord1)),
712 boost::endian::endian_reverse(std::get<3>(refLockRecord2)),
713 i)))
manojkiraneda0b631ae2019-12-03 17:54:28 +0530714 {
manojkiraneda0b631ae2019-12-03 17:54:28 +0530715 return false;
716 }
manojkiraneda0b631ae2019-12-03 17:54:28 +0530717 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700718
719 ++i;
manojkiraneda0b631ae2019-12-03 17:54:28 +0530720 }
721
722 return false;
723}
724
Ratan Gupta07386c62019-12-14 14:06:09 +0530725inline uint32_t Lock::generateTransactionId()
manojkiraneda0b631ae2019-12-03 17:54:28 +0530726{
727 ++transactionId;
728 return transactionId;
729}
730
731} // namespace ibm_mc_lock
732} // namespace crow