1 /** 2 * Copyright © 2016 IBM 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 #include "config.h" 17 18 #include "mainloop.hpp" 19 20 #include "env.hpp" 21 #include "fan_pwm.hpp" 22 #include "fan_speed.hpp" 23 #include "hwmon.hpp" 24 #include "hwmonio.hpp" 25 #include "sensor.hpp" 26 #include "sensorset.hpp" 27 #include "sysfs.hpp" 28 #include "targets.hpp" 29 #include "thresholds.hpp" 30 #include "util.hpp" 31 32 #include <phosphor-logging/elog-errors.hpp> 33 #include <xyz/openbmc_project/Sensor/Device/error.hpp> 34 35 #include <cassert> 36 #include <cstdlib> 37 #include <format> 38 #include <functional> 39 #include <future> 40 #include <iostream> 41 #include <memory> 42 #include <sstream> 43 #include <string> 44 #include <unordered_set> 45 46 using namespace phosphor::logging; 47 48 // Initialization for Warning Objects 49 decltype(Thresholds<WarningObject>::setLo) Thresholds<WarningObject>::setLo = 50 &WarningObject::warningLow; 51 decltype(Thresholds<WarningObject>::setHi) Thresholds<WarningObject>::setHi = 52 &WarningObject::warningHigh; 53 decltype(Thresholds<WarningObject>::getLo) Thresholds<WarningObject>::getLo = 54 &WarningObject::warningLow; 55 decltype(Thresholds<WarningObject>::getHi) Thresholds<WarningObject>::getHi = 56 &WarningObject::warningHigh; 57 decltype(Thresholds<WarningObject>::alarmLo) 58 Thresholds<WarningObject>::alarmLo = &WarningObject::warningAlarmLow; 59 decltype(Thresholds<WarningObject>::alarmHi) 60 Thresholds<WarningObject>::alarmHi = &WarningObject::warningAlarmHigh; 61 decltype(Thresholds<WarningObject>::getAlarmLow) 62 Thresholds<WarningObject>::getAlarmLow = &WarningObject::warningAlarmLow; 63 decltype(Thresholds<WarningObject>::getAlarmHigh) 64 Thresholds<WarningObject>::getAlarmHigh = &WarningObject::warningAlarmHigh; 65 decltype(Thresholds<WarningObject>::assertLowSignal) 66 Thresholds<WarningObject>::assertLowSignal = 67 &WarningObject::warningLowAlarmAsserted; 68 decltype(Thresholds<WarningObject>::assertHighSignal) 69 Thresholds<WarningObject>::assertHighSignal = 70 &WarningObject::warningHighAlarmAsserted; 71 decltype(Thresholds<WarningObject>::deassertLowSignal) 72 Thresholds<WarningObject>::deassertLowSignal = 73 &WarningObject::warningLowAlarmDeasserted; 74 decltype(Thresholds<WarningObject>::deassertHighSignal) 75 Thresholds<WarningObject>::deassertHighSignal = 76 &WarningObject::warningHighAlarmDeasserted; 77 78 // Initialization for Critical Objects 79 decltype(Thresholds<CriticalObject>::setLo) Thresholds<CriticalObject>::setLo = 80 &CriticalObject::criticalLow; 81 decltype(Thresholds<CriticalObject>::setHi) Thresholds<CriticalObject>::setHi = 82 &CriticalObject::criticalHigh; 83 decltype(Thresholds<CriticalObject>::getLo) Thresholds<CriticalObject>::getLo = 84 &CriticalObject::criticalLow; 85 decltype(Thresholds<CriticalObject>::getHi) Thresholds<CriticalObject>::getHi = 86 &CriticalObject::criticalHigh; 87 decltype(Thresholds<CriticalObject>::alarmLo) 88 Thresholds<CriticalObject>::alarmLo = &CriticalObject::criticalAlarmLow; 89 decltype(Thresholds<CriticalObject>::alarmHi) 90 Thresholds<CriticalObject>::alarmHi = &CriticalObject::criticalAlarmHigh; 91 decltype(Thresholds<CriticalObject>::getAlarmLow) 92 Thresholds<CriticalObject>::getAlarmLow = &CriticalObject::criticalAlarmLow; 93 decltype(Thresholds<CriticalObject>::getAlarmHigh) 94 Thresholds<CriticalObject>::getAlarmHigh = 95 &CriticalObject::criticalAlarmHigh; 96 decltype(Thresholds<CriticalObject>::assertLowSignal) 97 Thresholds<CriticalObject>::assertLowSignal = 98 &CriticalObject::criticalLowAlarmAsserted; 99 decltype(Thresholds<CriticalObject>::assertHighSignal) 100 Thresholds<CriticalObject>::assertHighSignal = 101 &CriticalObject::criticalHighAlarmAsserted; 102 decltype(Thresholds<CriticalObject>::deassertLowSignal) 103 Thresholds<CriticalObject>::deassertLowSignal = 104 &CriticalObject::criticalLowAlarmDeasserted; 105 decltype(Thresholds<CriticalObject>::deassertHighSignal) 106 Thresholds<CriticalObject>::deassertHighSignal = 107 &CriticalObject::criticalHighAlarmDeasserted; 108 109 void updateSensorInterfaces(InterfaceMap& ifaces, SensorValueType value) 110 { 111 for (auto& iface : ifaces) 112 { 113 switch (iface.first) 114 { 115 // clang-format off 116 case InterfaceType::VALUE: 117 { 118 auto& valueIface = 119 std::any_cast<std::shared_ptr<ValueObject>&>(iface.second); 120 valueIface->value(value); 121 } 122 break; 123 // clang-format on 124 case InterfaceType::WARN: 125 checkThresholds<WarningObject>(iface.second, value); 126 break; 127 case InterfaceType::CRIT: 128 checkThresholds<CriticalObject>(iface.second, value); 129 break; 130 default: 131 break; 132 } 133 } 134 } 135 136 std::string MainLoop::getID(SensorSet::container_t::const_reference sensor) 137 { 138 std::string id; 139 140 /* 141 * Check if the value of the MODE_<item><X> env variable for the sensor 142 * is set. If it is, then read the from the <item><X>_<mode> 143 * file. The name of the DBUS object would be the value of the env 144 * variable LABEL_<item><mode value>. If the MODE_<item><X> env variable 145 * doesn't exist, then the name of DBUS object is the value of the env 146 * variable LABEL_<item><X>. 147 * 148 * For example, if MODE_temp1 = "label", then code reads the temp1_label 149 * file. If it has a 5 in it, then it will use the following entry to 150 * name the object: LABEL_temp5 = "My DBus object name". 151 * 152 */ 153 auto mode = env::getEnv("MODE", sensor.first); 154 if (!mode.empty()) 155 { 156 id = env::getIndirectID(_hwmonRoot + '/' + _instance + '/', mode, 157 sensor.first); 158 159 if (id.empty()) 160 { 161 return id; 162 } 163 } 164 165 // Use the ID we looked up above if there was one, 166 // otherwise use the standard one. 167 id = (id.empty()) ? sensor.first.second : id; 168 169 return id; 170 } 171 172 SensorIdentifiers 173 MainLoop::getIdentifiers(SensorSet::container_t::const_reference sensor) 174 { 175 std::string id = getID(sensor); 176 std::string label; 177 std::string accuracy; 178 std::string priority; 179 180 if (!id.empty()) 181 { 182 // Ignore inputs without a label. 183 label = env::getEnv("LABEL", sensor.first.first, id); 184 accuracy = env::getEnv("ACCURACY", sensor.first.first, id); 185 priority = env::getEnv("PRIORITY", sensor.first.first, id); 186 } 187 188 return std::make_tuple(std::move(id), std::move(label), std::move(accuracy), 189 std::move(priority)); 190 } 191 192 /** 193 * Reads the environment parameters of a sensor and creates an object with 194 * at least the `Value` interface, otherwise returns without creating the 195 * object. If the `Value` interface is successfully created, by reading the 196 * sensor's corresponding sysfs file's value, the additional interfaces for 197 * the sensor are created and the InterfacesAdded signal is emitted. The 198 * object's state data is then returned for sensor state monitoring within 199 * the main loop. 200 */ 201 std::optional<ObjectStateData> 202 MainLoop::getObject(SensorSet::container_t::const_reference sensor) 203 { 204 auto properties = getIdentifiers(sensor); 205 if (std::get<sensorID>(properties).empty() || 206 std::get<sensorLabel>(properties).empty()) 207 { 208 return {}; 209 } 210 211 hwmon::Attributes attrs; 212 if (!hwmon::getAttributes(sensor.first.first, attrs)) 213 { 214 return {}; 215 } 216 217 const auto& [sensorSetKey, sensorAttrs] = sensor; 218 const auto& [sensorSysfsType, sensorSysfsNum] = sensorSetKey; 219 220 /* Note: The sensor objects all share the same ioAccess object. */ 221 auto sensorObj = 222 std::make_unique<sensor::Sensor>(sensorSetKey, _ioAccess, _devPath); 223 224 // Get list of return codes for removing sensors on device 225 auto devRmRCs = env::getEnv("REMOVERCS"); 226 // Add sensor removal return codes defined at the device level 227 sensorObj->addRemoveRCs(devRmRCs); 228 229 std::string objectPath{_root}; 230 objectPath.append(1, '/'); 231 objectPath.append(hwmon::getNamespace(attrs)); 232 objectPath.append(1, '/'); 233 objectPath.append(std::get<sensorLabel>(properties)); 234 235 ObjectInfo info(&_bus, std::move(objectPath), InterfaceMap()); 236 RetryIO retryIO(hwmonio::retries, hwmonio::delay); 237 if (_rmSensors.find(sensorSetKey) != _rmSensors.end()) 238 { 239 // When adding a sensor that was purposely removed, 240 // don't retry on errors when reading its value 241 std::get<size_t>(retryIO) = 0; 242 } 243 auto valueInterface = static_cast<std::shared_ptr<ValueObject>>(nullptr); 244 try 245 { 246 // Add accuracy interface 247 auto accuracyStr = std::get<sensorAccuracy>(properties); 248 try 249 { 250 if (!accuracyStr.empty()) 251 { 252 auto accuracy = stod(accuracyStr); 253 sensorObj->addAccuracy(info, accuracy); 254 } 255 } 256 catch (const std::invalid_argument&) 257 {} 258 259 // Add priority interface 260 auto priorityStr = std::get<sensorPriority>(properties); 261 try 262 { 263 if (!priorityStr.empty()) 264 { 265 auto priority = std::stoul(priorityStr); 266 sensorObj->addPriority(info, priority); 267 } 268 } 269 catch (const std::invalid_argument&) 270 {} 271 272 // Add status interface based on _fault file being present 273 sensorObj->addStatus(info); 274 valueInterface = sensorObj->addValue(retryIO, info, _timedoutMap); 275 } 276 catch (const std::system_error& e) 277 { 278 auto file = 279 sysfs::make_sysfs_path(_ioAccess->path(), sensorSysfsType, 280 sensorSysfsNum, hwmon::entry::cinput); 281 282 // Check sensorAdjusts for sensor removal RCs 283 auto& sAdjusts = sensorObj->getAdjusts(); 284 if (sAdjusts.rmRCs.count(e.code().value()) > 0) 285 { 286 // Return code found in sensor return code removal list 287 if (_rmSensors.find(sensorSetKey) == _rmSensors.end()) 288 { 289 // Trace for sensor not already removed from dbus 290 log<level::INFO>("Sensor not added to dbus for read fail", 291 entry("FILE=%s", file.c_str()), 292 entry("RC=%d", e.code().value())); 293 _rmSensors[std::move(sensorSetKey)] = std::move(sensorAttrs); 294 } 295 return {}; 296 } 297 298 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error; 299 report<ReadFailure>( 300 xyz::openbmc_project::Sensor::Device::ReadFailure::CALLOUT_ERRNO( 301 e.code().value()), 302 xyz::openbmc_project::Sensor::Device::ReadFailure:: 303 CALLOUT_DEVICE_PATH(_devPath.c_str())); 304 305 log<level::INFO>(std::format("Failing sysfs file: {} errno: {}", file, 306 e.code().value()) 307 .c_str()); 308 exit(EXIT_FAILURE); 309 } 310 auto sensorValue = valueInterface->value(); 311 int64_t scale = sensorObj->getScale(); 312 313 addThreshold<WarningObject>(sensorSysfsType, std::get<sensorID>(properties), 314 sensorValue, info, scale); 315 addThreshold<CriticalObject>(sensorSysfsType, 316 std::get<sensorID>(properties), sensorValue, 317 info, scale); 318 319 auto target = 320 addTarget<hwmon::FanSpeed>(sensorSetKey, _ioAccess, _devPath, info); 321 if (target) 322 { 323 target->enable(); 324 } 325 addTarget<hwmon::FanPwm>(sensorSetKey, _ioAccess, _devPath, info); 326 327 // All the interfaces have been created. Go ahead 328 // and emit InterfacesAdded. 329 valueInterface->emit_object_added(); 330 331 // Save sensor object specifications 332 _sensorObjects[sensorSetKey] = std::move(sensorObj); 333 334 return std::make_pair(std::move(std::get<sensorLabel>(properties)), 335 std::move(info)); 336 } 337 338 MainLoop::MainLoop(sdbusplus::bus_t&& bus, const std::string& param, 339 const std::string& path, const std::string& devPath, 340 const char* prefix, const char* root, 341 const std::string& instanceId, 342 const hwmonio::HwmonIOInterface* ioIntf) : 343 _bus(std::move(bus)), _manager(_bus, root), _pathParam(param), _hwmonRoot(), 344 _instance(), _devPath(devPath), _prefix(prefix), _root(root), _state(), 345 _instanceId(instanceId), _ioAccess(ioIntf), 346 _event(sdeventplus::Event::get_default()), 347 _timer(_event, std::bind(&MainLoop::read, this)) 348 { 349 // Strip off any trailing slashes. 350 std::string p = path; 351 while (!p.empty() && p.back() == '/') 352 { 353 p.pop_back(); 354 } 355 356 // Given the furthest right /, set instance to 357 // the basename, and hwmonRoot to the leading path. 358 auto n = p.rfind('/'); 359 if (n != std::string::npos) 360 { 361 _instance.assign(p.substr(n + 1)); 362 _hwmonRoot.assign(p.substr(0, n)); 363 } 364 365 assert(!_instance.empty()); 366 assert(!_hwmonRoot.empty()); 367 } 368 369 void MainLoop::shutdown() noexcept 370 { 371 _event.exit(0); 372 } 373 374 void MainLoop::run() 375 { 376 init(); 377 378 std::function<void()> callback(std::bind(&MainLoop::read, this)); 379 try 380 { 381 _timer.restart(std::chrono::microseconds(_interval)); 382 383 // TODO: Issue#6 - Optionally look at polling interval sysfs entry. 384 385 // TODO: Issue#7 - Should probably periodically check the SensorSet 386 // for new entries. 387 388 _bus.attach_event(_event.get(), SD_EVENT_PRIORITY_IMPORTANT); 389 _event.loop(); 390 } 391 catch (const std::exception& e) 392 { 393 log<level::ERR>("Error in sysfs polling loop", 394 entry("ERROR=%s", e.what())); 395 throw; 396 } 397 } 398 399 void MainLoop::init() 400 { 401 // Check sysfs for available sensors. 402 auto sensors = std::make_unique<SensorSet>(_hwmonRoot + '/' + _instance); 403 404 for (const auto& i : *sensors) 405 { 406 auto object = getObject(i); 407 if (object) 408 { 409 // Construct the SensorSet value 410 // std::tuple<SensorSet::mapped_type, 411 // std::string(Sensor Label), 412 // ObjectInfo> 413 auto value = 414 std::make_tuple(std::move(i.second), std::move((*object).first), 415 std::move((*object).second)); 416 417 _state[std::move(i.first)] = std::move(value); 418 } 419 420 // Initialize _averageMap of sensor. e.g. <<power, 1>, <0, 0>> 421 if ((i.first.first == hwmon::type::power) && 422 (phosphor::utility::isAverageEnvSet(i.first))) 423 { 424 _average.setAverageValue(i.first, std::make_pair(0, 0)); 425 } 426 } 427 428 /* If there are no sensors specified by labels, exit. */ 429 if (0 == _state.size()) 430 { 431 exit(0); 432 } 433 434 { 435 std::stringstream ss; 436 std::string id = _instanceId; 437 if (id.empty()) 438 { 439 id = 440 std::to_string(std::hash<std::string>{}(_devPath + _pathParam)); 441 } 442 ss << _prefix << "-" << id << ".Hwmon1"; 443 444 _bus.request_name(ss.str().c_str()); 445 } 446 447 { 448 auto interval = env::getEnv("INTERVAL"); 449 if (!interval.empty()) 450 { 451 _interval = std::strtoull(interval.c_str(), NULL, 10); 452 } 453 } 454 } 455 456 void MainLoop::read() 457 { 458 // TODO: Issue#3 - Need to make calls to the dbus sensor cache here to 459 // ensure the objects all exist? 460 461 // Iterate through all the sensors. 462 for (auto& [sensorSetKey, sensorStateTuple] : _state) 463 { 464 const auto& [sensorSysfsType, sensorSysfsNum] = sensorSetKey; 465 auto& [attrs, unused, objInfo] = sensorStateTuple; 466 467 if (attrs.find(hwmon::entry::input) == attrs.end()) 468 { 469 continue; 470 } 471 472 // Read value from sensor. 473 std::string input = hwmon::entry::input; 474 if (sensorSysfsType == hwmon::type::pwm) 475 { 476 input = ""; 477 } 478 // If type is power and AVERAGE_power* is true in env, use average 479 // instead of input 480 else if ((sensorSysfsType == hwmon::type::power) && 481 (phosphor::utility::isAverageEnvSet(sensorSetKey))) 482 { 483 input = hwmon::entry::average; 484 } 485 486 SensorValueType value; 487 auto& obj = std::get<InterfaceMap>(objInfo); 488 std::unique_ptr<sensor::Sensor>& sensor = _sensorObjects[sensorSetKey]; 489 490 auto& statusIface = std::any_cast<std::shared_ptr<StatusObject>&>( 491 obj[InterfaceType::STATUS]); 492 // As long as addStatus is called before addValue, statusIface 493 // should never be nullptr. 494 assert(statusIface); 495 496 try 497 { 498 if (sensor->hasFaultFile()) 499 { 500 auto fault = _ioAccess->read(sensorSysfsType, sensorSysfsNum, 501 hwmon::entry::fault, 502 hwmonio::retries, hwmonio::delay); 503 // Skip reading from a sensor with a valid fault file 504 // and set the functional property accordingly 505 if (!statusIface->functional((fault == 0) ? true : false)) 506 { 507 continue; 508 } 509 } 510 511 { 512 // RAII object for GPIO unlock / lock 513 auto locker = sensor::gpioUnlock(sensor->getGpio()); 514 515 // For sensors with attribute ASYNC_READ_TIMEOUT, 516 // spawn a thread with timeout 517 auto asyncReadTimeout = 518 env::getEnv("ASYNC_READ_TIMEOUT", sensorSetKey); 519 if (!asyncReadTimeout.empty()) 520 { 521 std::chrono::milliseconds asyncTimeout{ 522 std::stoi(asyncReadTimeout)}; 523 value = sensor::asyncRead( 524 sensorSetKey, _ioAccess, asyncTimeout, _timedoutMap, 525 sensorSysfsType, sensorSysfsNum, input, 526 hwmonio::retries, hwmonio::delay); 527 } 528 else 529 { 530 // Retry for up to a second if device is busy 531 // or has a transient error. 532 value = 533 _ioAccess->read(sensorSysfsType, sensorSysfsNum, input, 534 hwmonio::retries, hwmonio::delay); 535 } 536 537 // Set functional property to true if we could read sensor 538 statusIface->functional(true); 539 540 value = sensor->adjustValue(value); 541 542 if (input == hwmon::entry::average) 543 { 544 // Calculate the values of averageMap based on current 545 // average value, current average_interval value, previous 546 // average value, previous average_interval value 547 int64_t interval = 548 _ioAccess->read(sensorSysfsType, sensorSysfsNum, 549 hwmon::entry::caverage_interval, 550 hwmonio::retries, hwmonio::delay); 551 auto ret = _average.getAverageValue(sensorSetKey); 552 assert(ret); 553 554 const auto& [preAverage, preInterval] = *ret; 555 556 auto calValue = Average::calcAverage( 557 preAverage, preInterval, value, interval); 558 if (calValue) 559 { 560 // Update previous values in averageMap before the 561 // variable value is changed next 562 _average.setAverageValue( 563 sensorSetKey, std::make_pair(value, interval)); 564 // Update value to be calculated average 565 value = calValue.value(); 566 } 567 else 568 { 569 // the value of 570 // power*_average_interval is not changed yet, use the 571 // previous calculated average instead. So skip dbus 572 // update. 573 continue; 574 } 575 } 576 } 577 578 updateSensorInterfaces(obj, value); 579 } 580 catch (const std::system_error& e) 581 { 582 #if UPDATE_FUNCTIONAL_ON_FAIL 583 // If UPDATE_FUNCTIONAL_ON_FAIL is defined and an exception was 584 // thrown, set the functional property to false. 585 // We cannot set this with the 'continue' in the lower block 586 // as the code may exit before reaching it. 587 statusIface->functional(false); 588 #endif 589 auto file = sysfs::make_sysfs_path( 590 _ioAccess->path(), sensorSysfsType, sensorSysfsNum, input); 591 592 // Check sensorAdjusts for sensor removal RCs 593 auto& sAdjusts = _sensorObjects[sensorSetKey]->getAdjusts(); 594 if (sAdjusts.rmRCs.count(e.code().value()) > 0) 595 { 596 // Return code found in sensor return code removal list 597 if (_rmSensors.find(sensorSetKey) == _rmSensors.end()) 598 { 599 // Trace for sensor not already removed from dbus 600 log<level::INFO>("Remove sensor from dbus for read fail", 601 entry("FILE=%s", file.c_str()), 602 entry("RC=%d", e.code().value())); 603 // Mark this sensor to be removed from dbus 604 _rmSensors[sensorSetKey] = attrs; 605 } 606 continue; 607 } 608 #if UPDATE_FUNCTIONAL_ON_FAIL 609 // Do not exit with failure if UPDATE_FUNCTIONAL_ON_FAIL is set 610 continue; 611 #endif 612 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device:: 613 Error; 614 report<ReadFailure>( 615 xyz::openbmc_project::Sensor::Device::ReadFailure:: 616 CALLOUT_ERRNO(e.code().value()), 617 xyz::openbmc_project::Sensor::Device::ReadFailure:: 618 CALLOUT_DEVICE_PATH(_devPath.c_str())); 619 620 log<level::INFO>(std::format("Failing sysfs file: {} errno: {}", 621 file, e.code().value()) 622 .c_str()); 623 624 exit(EXIT_FAILURE); 625 } 626 } 627 628 removeSensors(); 629 630 addDroppedSensors(); 631 } 632 633 void MainLoop::removeSensors() 634 { 635 // Remove any sensors marked for removal 636 for (const auto& i : _rmSensors) 637 { 638 // Remove sensor object from dbus using emit_object_removed() 639 auto& objInfo = std::get<ObjectInfo>(_state[i.first]); 640 auto& objPath = std::get<std::string>(objInfo); 641 642 _bus.emit_object_removed(objPath.c_str()); 643 644 // Erase sensor object info 645 _state.erase(i.first); 646 } 647 } 648 649 void MainLoop::addDroppedSensors() 650 { 651 // Attempt to add any sensors that were removed 652 auto it = _rmSensors.begin(); 653 while (it != _rmSensors.end()) 654 { 655 if (_state.find(it->first) == _state.end()) 656 { 657 SensorSet::container_t::value_type ssValueType = 658 std::make_pair(it->first, it->second); 659 660 auto object = getObject(ssValueType); 661 if (object) 662 { 663 // Construct the SensorSet value 664 // std::tuple<SensorSet::mapped_type, 665 // std::string(Sensor Label), 666 // ObjectInfo> 667 auto value = std::make_tuple(std::move(ssValueType.second), 668 std::move((*object).first), 669 std::move((*object).second)); 670 671 _state[std::move(ssValueType.first)] = std::move(value); 672 673 std::string input = hwmon::entry::input; 674 // If type is power and AVERAGE_power* is true in env, use 675 // average instead of input 676 if ((it->first.first == hwmon::type::power) && 677 (phosphor::utility::isAverageEnvSet(it->first))) 678 { 679 input = hwmon::entry::average; 680 } 681 // Sensor object added, erase entry from removal list 682 auto file = 683 sysfs::make_sysfs_path(_ioAccess->path(), it->first.first, 684 it->first.second, input); 685 686 log<level::INFO>("Added sensor to dbus after successful read", 687 entry("FILE=%s", file.c_str())); 688 689 it = _rmSensors.erase(it); 690 } 691 else 692 { 693 ++it; 694 } 695 } 696 else 697 { 698 // Sanity check to remove sensors that were re-added 699 it = _rmSensors.erase(it); 700 } 701 } 702 } 703 704 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 705