blob: d148d9a7f5c4912b2628031f19ed6988bf7cebc8 [file] [log] [blame]
Willy Tude54f482021-01-26 15:59:09 -08001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include "dbus-sdr/sdrutils.hpp"
18
Willy Tu4eca2512022-06-20 21:14:51 -070019#include <optional>
20#include <unordered_set>
21
Scron Chang2703b022021-07-06 15:47:45 +080022#ifdef FEATURE_HYBRID_SENSORS
23
24#include <ipmid/utils.hpp>
25namespace ipmi
26{
27namespace sensor
28{
29extern const IdInfoMap sensors;
30} // namespace sensor
31} // namespace ipmi
32
33#endif
34
Willy Tude54f482021-01-26 15:59:09 -080035namespace details
36{
Kuiying Wanga8b5b262021-02-06 23:38:22 +080037uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree)
Willy Tude54f482021-01-26 15:59:09 -080038{
39 static std::shared_ptr<SensorSubTree> sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +080040 static uint16_t sensorUpdatedIndex = 0;
Willy Tude54f482021-01-26 15:59:09 -080041 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williams5d82f472022-07-22 19:26:53 -050042 static sdbusplus::bus::match_t sensorAdded(
Willy Tude54f482021-01-26 15:59:09 -080043 *dbus,
44 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
45 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -050046 [](sdbusplus::message_t&) { sensorTreePtr.reset(); });
Willy Tude54f482021-01-26 15:59:09 -080047
Patrick Williams5d82f472022-07-22 19:26:53 -050048 static sdbusplus::bus::match_t sensorRemoved(
Willy Tude54f482021-01-26 15:59:09 -080049 *dbus,
50 "type='signal',member='InterfacesRemoved',arg0path='/xyz/"
51 "openbmc_project/sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -050052 [](sdbusplus::message_t&) { sensorTreePtr.reset(); });
Willy Tude54f482021-01-26 15:59:09 -080053
Willy Tude54f482021-01-26 15:59:09 -080054 if (sensorTreePtr)
55 {
56 subtree = sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +080057 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -080058 }
59
60 sensorTreePtr = std::make_shared<SensorSubTree>();
61
Willy Tude54f482021-01-26 15:59:09 -080062 static constexpr const int32_t depth = 2;
Hao Jiang9a5b51e2021-01-06 10:32:22 -080063
Patrick Williams369824e2023-10-20 11:18:23 -050064 auto lbdUpdateSensorTree = [&dbus](const char* path,
65 const auto& interfaces) {
Hao Jiang9a5b51e2021-01-06 10:32:22 -080066 auto mapperCall = dbus->new_method_call(
67 "xyz.openbmc_project.ObjectMapper",
68 "/xyz/openbmc_project/object_mapper",
69 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
70 SensorSubTree sensorTreePartial;
71
72 mapperCall.append(path, depth, interfaces);
73
74 try
75 {
76 auto mapperReply = dbus->call(mapperCall);
77 mapperReply.read(sensorTreePartial);
78 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -050079 catch (const sdbusplus::exception_t& e)
Hao Jiang9a5b51e2021-01-06 10:32:22 -080080 {
81 phosphor::logging::log<phosphor::logging::level::ERR>(
82 "fail to update subtree",
83 phosphor::logging::entry("PATH=%s", path),
84 phosphor::logging::entry("WHAT=%s", e.what()));
85 return false;
86 }
87 if constexpr (debug)
88 {
89 std::fprintf(stderr, "IPMI updated: %zu sensors under %s\n",
90 sensorTreePartial.size(), path);
91 }
92 sensorTreePtr->merge(std::move(sensorTreePartial));
93 return true;
94 };
95
96 // Add sensors to SensorTree
97 static constexpr const std::array sensorInterfaces = {
Willy Tude54f482021-01-26 15:59:09 -080098 "xyz.openbmc_project.Sensor.Value",
Jie Yangf0a89942021-07-29 15:30:25 -070099 "xyz.openbmc_project.Sensor.ValueMutability",
Willy Tude54f482021-01-26 15:59:09 -0800100 "xyz.openbmc_project.Sensor.Threshold.Warning",
101 "xyz.openbmc_project.Sensor.Threshold.Critical"};
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800102 static constexpr const std::array vrInterfaces = {
103 "xyz.openbmc_project.Control.VoltageRegulatorMode"};
Willy Tude54f482021-01-26 15:59:09 -0800104
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500105 bool sensorRez = lbdUpdateSensorTree("/xyz/openbmc_project/sensors",
106 sensorInterfaces);
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800107
Scron Chang2703b022021-07-06 15:47:45 +0800108#ifdef FEATURE_HYBRID_SENSORS
109
110 if (!ipmi::sensor::sensors.empty())
111 {
112 for (const auto& sensor : ipmi::sensor::sensors)
113 {
114 // Threshold sensors should not be emplaced in here.
115 if (boost::starts_with(sensor.second.sensorPath,
116 "/xyz/openbmc_project/sensors/"))
117 {
118 continue;
119 }
120
121 // The bus service name is not listed in ipmi::sensor::Info. Give it
122 // an empty string. For those function using non-threshold sensors,
123 // the bus service name will be retrieved in an alternative way.
124 boost::container::flat_map<std::string, std::vector<std::string>>
125 connectionMap{
126 {"", {sensor.second.propertyInterfaces.begin()->first}}};
127 sensorTreePtr->emplace(sensor.second.sensorPath, connectionMap);
128 }
129 }
130
131#endif
132
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800133 // Error if searching for sensors failed.
134 if (!sensorRez)
Willy Tude54f482021-01-26 15:59:09 -0800135 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800136 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800137 }
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800138
139 // Add VR control as optional search path.
140 (void)lbdUpdateSensorTree("/xyz/openbmc_project/vr", vrInterfaces);
141
Willy Tude54f482021-01-26 15:59:09 -0800142 subtree = sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800143 sensorUpdatedIndex++;
Josh Lehana55c9532020-10-28 21:59:06 -0700144 // The SDR is being regenerated, wipe the old stats
145 sdrStatsTable.wipeTable();
Jie Yangf0a89942021-07-29 15:30:25 -0700146 sdrWriteTable.wipeTable();
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800147 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800148}
149
150bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap)
151{
152 static std::shared_ptr<SensorNumMap> sensorNumMapPtr;
153 bool sensorNumMapUpated = false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800154 static uint16_t prevSensorUpdatedIndex = 0;
Willy Tude54f482021-01-26 15:59:09 -0800155 std::shared_ptr<SensorSubTree> sensorTree;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800156 uint16_t curSensorUpdatedIndex = details::getSensorSubtree(sensorTree);
Willy Tude54f482021-01-26 15:59:09 -0800157 if (!sensorTree)
158 {
159 return sensorNumMapUpated;
160 }
161
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800162 if ((curSensorUpdatedIndex == prevSensorUpdatedIndex) && sensorNumMapPtr)
Willy Tude54f482021-01-26 15:59:09 -0800163 {
164 sensorNumMap = sensorNumMapPtr;
165 return sensorNumMapUpated;
166 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800167 prevSensorUpdatedIndex = curSensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800168
169 sensorNumMapPtr = std::make_shared<SensorNumMap>();
170
171 uint16_t sensorNum = 0;
172 uint16_t sensorIndex = 0;
173 for (const auto& sensor : *sensorTree)
174 {
175 sensorNumMapPtr->insert(
176 SensorNumMap::value_type(sensorNum, sensor.first));
177 sensorIndex++;
178 if (sensorIndex == maxSensorsPerLUN)
179 {
180 sensorIndex = lun1Sensor0;
181 }
182 else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN))
183 {
184 // Skip assigning LUN 0x2 any sensors
185 sensorIndex = lun3Sensor0;
186 }
187 else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN))
188 {
189 // this is an error, too many IPMI sensors
190 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
191 }
192 sensorNum = sensorIndex;
193 }
194 sensorNumMap = sensorNumMapPtr;
195 sensorNumMapUpated = true;
196 return sensorNumMapUpated;
197}
198} // namespace details
199
200bool getSensorSubtree(SensorSubTree& subtree)
201{
202 std::shared_ptr<SensorSubTree> sensorTree;
203 details::getSensorSubtree(sensorTree);
204 if (!sensorTree)
205 {
206 return false;
207 }
208
209 subtree = *sensorTree;
210 return true;
211}
212
Scron Chang2703b022021-07-06 15:47:45 +0800213#ifdef FEATURE_HYBRID_SENSORS
214// Static sensors are listed in sensor-gen.cpp.
215ipmi::sensor::IdInfoMap::const_iterator
216 findStaticSensor(const std::string& path)
217{
218 return std::find_if(
219 ipmi::sensor::sensors.begin(), ipmi::sensor::sensors.end(),
220 [&path](const ipmi::sensor::IdInfoMap::value_type& findSensor) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500221 return findSensor.second.sensorPath == path;
Patrick Williams369824e2023-10-20 11:18:23 -0500222 });
Scron Chang2703b022021-07-06 15:47:45 +0800223}
224#endif
225
Willy Tude54f482021-01-26 15:59:09 -0800226std::string getSensorTypeStringFromPath(const std::string& path)
227{
228 // get sensor type string from path, path is defined as
229 // /xyz/openbmc_project/sensors/<type>/label
230 size_t typeEnd = path.rfind("/");
231 if (typeEnd == std::string::npos)
232 {
233 return path;
234 }
235 size_t typeStart = path.rfind("/", typeEnd - 1);
236 if (typeStart == std::string::npos)
237 {
238 return path;
239 }
240 // Start at the character after the '/'
241 typeStart++;
242 return path.substr(typeStart, typeEnd - typeStart);
243}
244
245uint8_t getSensorTypeFromPath(const std::string& path)
246{
247 uint8_t sensorType = 0;
248 std::string type = getSensorTypeStringFromPath(path);
249 auto findSensor = sensorTypes.find(type.c_str());
250 if (findSensor != sensorTypes.end())
251 {
Scron Chang2b42d7e2021-07-06 15:45:47 +0800252 sensorType =
253 static_cast<uint8_t>(std::get<sensorTypeCodes>(findSensor->second));
Willy Tude54f482021-01-26 15:59:09 -0800254 } // else default 0x0 RESERVED
255
256 return sensorType;
257}
258
259uint16_t getSensorNumberFromPath(const std::string& path)
260{
261 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
262 details::getSensorNumMap(sensorNumMapPtr);
263 if (!sensorNumMapPtr)
264 {
265 return invalidSensorNumber;
266 }
267
268 try
269 {
270 return sensorNumMapPtr->right.at(path);
271 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500272 catch (const std::out_of_range& e)
Willy Tude54f482021-01-26 15:59:09 -0800273 {
Willy Tude54f482021-01-26 15:59:09 -0800274 return invalidSensorNumber;
275 }
276}
277
278uint8_t getSensorEventTypeFromPath(const std::string& path)
279{
Scron Chang2b42d7e2021-07-06 15:45:47 +0800280 uint8_t sensorEventType = 0;
281 std::string type = getSensorTypeStringFromPath(path);
282 auto findSensor = sensorTypes.find(type.c_str());
283 if (findSensor != sensorTypes.end())
284 {
285 sensorEventType = static_cast<uint8_t>(
286 std::get<sensorEventTypeCodes>(findSensor->second));
287 }
288
289 return sensorEventType;
Willy Tude54f482021-01-26 15:59:09 -0800290}
291
292std::string getPathFromSensorNumber(uint16_t sensorNum)
293{
294 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
295 details::getSensorNumMap(sensorNumMapPtr);
296 if (!sensorNumMapPtr)
297 {
298 return std::string();
299 }
300
301 try
302 {
303 return sensorNumMapPtr->left.at(sensorNum);
304 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500305 catch (const std::out_of_range& e)
Willy Tude54f482021-01-26 15:59:09 -0800306 {
Willy Tude54f482021-01-26 15:59:09 -0800307 return std::string();
308 }
309}
310
311namespace ipmi
312{
313
314std::map<std::string, std::vector<std::string>>
315 getObjectInterfaces(const char* path)
316{
317 std::map<std::string, std::vector<std::string>> interfacesResponse;
318 std::vector<std::string> interfaces;
319 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
320
Patrick Williams5d82f472022-07-22 19:26:53 -0500321 sdbusplus::message_t getObjectMessage =
Willy Tude54f482021-01-26 15:59:09 -0800322 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
323 "/xyz/openbmc_project/object_mapper",
324 "xyz.openbmc_project.ObjectMapper", "GetObject");
325 getObjectMessage.append(path, interfaces);
326
327 try
328 {
Patrick Williams5d82f472022-07-22 19:26:53 -0500329 sdbusplus::message_t response = dbus->call(getObjectMessage);
Willy Tude54f482021-01-26 15:59:09 -0800330 response.read(interfacesResponse);
331 }
332 catch (const std::exception& e)
333 {
334 phosphor::logging::log<phosphor::logging::level::ERR>(
335 "Failed to GetObject", phosphor::logging::entry("PATH=%s", path),
336 phosphor::logging::entry("WHAT=%s", e.what()));
337 }
338
339 return interfacesResponse;
340}
341
342std::map<std::string, Value> getEntityManagerProperties(const char* path,
343 const char* interface)
344{
345 std::map<std::string, Value> properties;
346 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
347
Patrick Williams5d82f472022-07-22 19:26:53 -0500348 sdbusplus::message_t getProperties =
Willy Tude54f482021-01-26 15:59:09 -0800349 dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
350 "org.freedesktop.DBus.Properties", "GetAll");
351 getProperties.append(interface);
352
353 try
354 {
Patrick Williams5d82f472022-07-22 19:26:53 -0500355 sdbusplus::message_t response = dbus->call(getProperties);
Willy Tude54f482021-01-26 15:59:09 -0800356 response.read(properties);
357 }
358 catch (const std::exception& e)
359 {
360 phosphor::logging::log<phosphor::logging::level::ERR>(
361 "Failed to GetAll", phosphor::logging::entry("PATH=%s", path),
362 phosphor::logging::entry("INTF=%s", interface),
363 phosphor::logging::entry("WHAT=%s", e.what()));
364 }
365
366 return properties;
367}
368
Willy Tu4eca2512022-06-20 21:14:51 -0700369// Fetch the ipmiDecoratorPaths to get the list of dbus objects that
370// have ipmi decorator to prevent unnessary dbus call to fetch the info
371std::optional<std::unordered_set<std::string>>&
372 getIpmiDecoratorPaths(const std::optional<ipmi::Context::ptr>& ctx)
373{
374 static std::optional<std::unordered_set<std::string>> ipmiDecoratorPaths;
375
376 if (!ctx.has_value() || ipmiDecoratorPaths != std::nullopt)
377 {
378 return ipmiDecoratorPaths;
379 }
380
381 boost::system::error_code ec;
382 std::vector<std::string> paths =
383 (*ctx)->bus->yield_method_call<std::vector<std::string>>(
384 (*ctx)->yield, ec, "xyz.openbmc_project.ObjectMapper",
385 "/xyz/openbmc_project/object_mapper",
386 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/",
387 int32_t(0),
388 std::array<const char*, 1>{
389 "xyz.openbmc_project.Inventory.Decorator.Ipmi"});
390 if (ec)
391 {
392 return ipmiDecoratorPaths;
393 }
394
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500395 ipmiDecoratorPaths = std::unordered_set<std::string>(paths.begin(),
396 paths.end());
Willy Tu4eca2512022-06-20 21:14:51 -0700397 return ipmiDecoratorPaths;
398}
399
Willy Tude54f482021-01-26 15:59:09 -0800400const std::string* getSensorConfigurationInterface(
401 const std::map<std::string, std::vector<std::string>>&
402 sensorInterfacesResponse)
403{
404 auto entityManagerService =
405 sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
406 if (entityManagerService == sensorInterfacesResponse.end())
407 {
408 return nullptr;
409 }
410
411 // Find the fan configuration first (fans can have multiple configuration
412 // interfaces).
413 for (const auto& entry : entityManagerService->second)
414 {
415 if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
416 entry == "xyz.openbmc_project.Configuration.I2CFan" ||
417 entry == "xyz.openbmc_project.Configuration.NuvotonFan")
418 {
419 return &entry;
420 }
421 }
422
423 for (const auto& entry : entityManagerService->second)
424 {
425 if (boost::algorithm::starts_with(entry,
426 "xyz.openbmc_project.Configuration."))
427 {
428 return &entry;
429 }
430 }
431
432 return nullptr;
433}
434
435// Follow Association properties for Sensor back to the Board dbus object to
436// check for an EntityId and EntityInstance property.
Willy Tu4eca2512022-06-20 21:14:51 -0700437void updateIpmiFromAssociation(
438 const std::string& path,
439 const std::unordered_set<std::string>& ipmiDecoratorPaths,
440 const DbusInterfaceMap& sensorMap, uint8_t& entityId,
441 uint8_t& entityInstance)
Willy Tude54f482021-01-26 15:59:09 -0800442{
443 namespace fs = std::filesystem;
444
445 auto sensorAssociationObject =
446 sensorMap.find("xyz.openbmc_project.Association.Definitions");
447 if (sensorAssociationObject == sensorMap.end())
448 {
449 if constexpr (debug)
450 {
451 std::fprintf(stderr, "path=%s, no association interface found\n",
452 path.c_str());
453 }
454
455 return;
456 }
457
458 auto associationObject =
459 sensorAssociationObject->second.find("Associations");
460 if (associationObject == sensorAssociationObject->second.end())
461 {
462 if constexpr (debug)
463 {
464 std::fprintf(stderr, "path=%s, no association records found\n",
465 path.c_str());
466 }
467
468 return;
469 }
470
471 std::vector<Association> associationValues =
472 std::get<std::vector<Association>>(associationObject->second);
473
474 // loop through the Associations looking for the right one:
475 for (const auto& entry : associationValues)
476 {
477 // forward, reverse, endpoint
478 const std::string& forward = std::get<0>(entry);
479 const std::string& reverse = std::get<1>(entry);
480 const std::string& endpoint = std::get<2>(entry);
481
482 // We only currently concern ourselves with chassis+all_sensors.
483 if (!(forward == "chassis" && reverse == "all_sensors"))
484 {
485 continue;
486 }
487
488 // the endpoint is the board entry provided by
489 // Entity-Manager. so let's grab its properties if it has
490 // the right interface.
491
492 // just try grabbing the properties first.
Willy Tu4eca2512022-06-20 21:14:51 -0700493 ipmi::PropertyMap::iterator entityIdProp;
494 ipmi::PropertyMap::iterator entityInstanceProp;
495 if (ipmiDecoratorPaths.contains(endpoint))
496 {
497 std::map<std::string, Value> ipmiProperties =
498 getEntityManagerProperties(
499 endpoint.c_str(),
500 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
Willy Tude54f482021-01-26 15:59:09 -0800501
Willy Tu4eca2512022-06-20 21:14:51 -0700502 entityIdProp = ipmiProperties.find("EntityId");
503 entityInstanceProp = ipmiProperties.find("EntityInstance");
504 if (entityIdProp != ipmiProperties.end())
505 {
506 entityId = static_cast<uint8_t>(
507 std::get<uint64_t>(entityIdProp->second));
508 }
509 if (entityInstanceProp != ipmiProperties.end())
510 {
511 entityInstance = static_cast<uint8_t>(
512 std::get<uint64_t>(entityInstanceProp->second));
513 }
Willy Tude54f482021-01-26 15:59:09 -0800514 }
515
516 // Now check the entity-manager entry for this sensor to see
517 // if it has its own value and use that instead.
518 //
519 // In theory, checking this first saves us from checking
520 // both, except in most use-cases identified, there won't be
521 // a per sensor override, so we need to always check both.
522 std::string sensorNameFromPath = fs::path(path).filename();
523
524 std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
525
526 // Download the interfaces for the sensor from
527 // Entity-Manager to find the name of the configuration
528 // interface.
529 std::map<std::string, std::vector<std::string>>
530 sensorInterfacesResponse =
531 getObjectInterfaces(sensorConfigPath.c_str());
532
533 const std::string* configurationInterface =
534 getSensorConfigurationInterface(sensorInterfacesResponse);
535
Harvey Wu7bb84db2023-06-26 10:02:08 +0800536 // If there are multi association path settings and only one path exist,
537 // we need to continue if cannot find configuration interface for this
538 // sensor.
Willy Tude54f482021-01-26 15:59:09 -0800539 if (!configurationInterface)
540 {
Harvey Wu7bb84db2023-06-26 10:02:08 +0800541 continue;
Willy Tude54f482021-01-26 15:59:09 -0800542 }
543
544 // We found a configuration interface.
545 std::map<std::string, Value> configurationProperties =
546 getEntityManagerProperties(sensorConfigPath.c_str(),
547 configurationInterface->c_str());
548
549 entityIdProp = configurationProperties.find("EntityId");
550 entityInstanceProp = configurationProperties.find("EntityInstance");
551 if (entityIdProp != configurationProperties.end())
552 {
553 entityId =
554 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
555 }
556 if (entityInstanceProp != configurationProperties.end())
557 {
558 entityInstance = static_cast<uint8_t>(
559 std::get<uint64_t>(entityInstanceProp->second));
560 }
561
562 // stop searching Association records.
563 break;
564 } // end for Association vectors.
565
566 if constexpr (debug)
567 {
568 std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
569 path.c_str(), entityId, entityInstance);
570 }
571}
572
573} // namespace ipmi