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 * atleast the `Value` interface, otherwise returns without creating the object. 195 * If the `Value` interface is successfully created, by reading the sensor's 196 * corresponding sysfs file's value, the additional interfaces for the sensor 197 * are created and the InterfacesAdded signal is emitted. The object's state 198 * data is then returned for sensor state monitoring within the main loop. 199 */ 200 std::optional<ObjectStateData> 201 MainLoop::getObject(SensorSet::container_t::const_reference sensor) 202 { 203 auto properties = getIdentifiers(sensor); 204 if (std::get<sensorID>(properties).empty() || 205 std::get<sensorLabel>(properties).empty()) 206 { 207 return {}; 208 } 209 210 hwmon::Attributes attrs; 211 if (!hwmon::getAttributes(sensor.first.first, attrs)) 212 { 213 return {}; 214 } 215 216 const auto& [sensorSetKey, sensorAttrs] = sensor; 217 const auto& [sensorSysfsType, sensorSysfsNum] = sensorSetKey; 218 219 /* Note: The sensor objects all share the same ioAccess object. */ 220 auto sensorObj = 221 std::make_unique<sensor::Sensor>(sensorSetKey, _ioAccess, _devPath); 222 223 // Get list of return codes for removing sensors on device 224 auto devRmRCs = env::getEnv("REMOVERCS"); 225 // Add sensor removal return codes defined at the device level 226 sensorObj->addRemoveRCs(devRmRCs); 227 228 std::string objectPath{_root}; 229 objectPath.append(1, '/'); 230 objectPath.append(hwmon::getNamespace(attrs)); 231 objectPath.append(1, '/'); 232 objectPath.append(std::get<sensorLabel>(properties)); 233 234 ObjectInfo info(&_bus, std::move(objectPath), InterfaceMap()); 235 RetryIO retryIO(hwmonio::retries, hwmonio::delay); 236 if (_rmSensors.find(sensorSetKey) != _rmSensors.end()) 237 { 238 // When adding a sensor that was purposely removed, 239 // don't retry on errors when reading its value 240 std::get<size_t>(retryIO) = 0; 241 } 242 auto valueInterface = static_cast<std::shared_ptr<ValueObject>>(nullptr); 243 try 244 { 245 // Add accuracy interface 246 auto accuracyStr = std::get<sensorAccuracy>(properties); 247 try 248 { 249 if (!accuracyStr.empty()) 250 { 251 auto accuracy = stod(accuracyStr); 252 sensorObj->addAccuracy(info, accuracy); 253 } 254 } 255 catch (const std::invalid_argument&) 256 {} 257 258 // Add priority interface 259 auto priorityStr = std::get<sensorPriority>(properties); 260 try 261 { 262 if (!priorityStr.empty()) 263 { 264 auto priority = std::stoul(priorityStr); 265 sensorObj->addPriority(info, priority); 266 } 267 } 268 catch (const std::invalid_argument&) 269 {} 270 271 // Add status interface based on _fault file being present 272 sensorObj->addStatus(info); 273 valueInterface = sensorObj->addValue(retryIO, info, _timedoutMap); 274 } 275 catch (const std::system_error& e) 276 { 277 auto file = 278 sysfs::make_sysfs_path(_ioAccess->path(), sensorSysfsType, 279 sensorSysfsNum, hwmon::entry::cinput); 280 281 // Check sensorAdjusts for sensor removal RCs 282 auto& sAdjusts = sensorObj->getAdjusts(); 283 if (sAdjusts.rmRCs.count(e.code().value()) > 0) 284 { 285 // Return code found in sensor return code removal list 286 if (_rmSensors.find(sensorSetKey) == _rmSensors.end()) 287 { 288 // Trace for sensor not already removed from dbus 289 log<level::INFO>("Sensor not added to dbus for read fail", 290 entry("FILE=%s", file.c_str()), 291 entry("RC=%d", e.code().value())); 292 _rmSensors[std::move(sensorSetKey)] = std::move(sensorAttrs); 293 } 294 return {}; 295 } 296 297 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error; 298 report<ReadFailure>( 299 xyz::openbmc_project::Sensor::Device::ReadFailure::CALLOUT_ERRNO( 300 e.code().value()), 301 xyz::openbmc_project::Sensor::Device::ReadFailure:: 302 CALLOUT_DEVICE_PATH(_devPath.c_str())); 303 304 log<level::INFO>(std::format("Failing sysfs file: {} errno: {}", file, 305 e.code().value()) 306 .c_str()); 307 exit(EXIT_FAILURE); 308 } 309 auto sensorValue = valueInterface->value(); 310 int64_t scale = sensorObj->getScale(); 311 312 addThreshold<WarningObject>(sensorSysfsType, std::get<sensorID>(properties), 313 sensorValue, info, scale); 314 addThreshold<CriticalObject>(sensorSysfsType, 315 std::get<sensorID>(properties), sensorValue, 316 info, scale); 317 318 auto target = 319 addTarget<hwmon::FanSpeed>(sensorSetKey, _ioAccess, _devPath, info); 320 if (target) 321 { 322 target->enable(); 323 } 324 addTarget<hwmon::FanPwm>(sensorSetKey, _ioAccess, _devPath, info); 325 326 // All the interfaces have been created. Go ahead 327 // and emit InterfacesAdded. 328 valueInterface->emit_object_added(); 329 330 // Save sensor object specifications 331 _sensorObjects[sensorSetKey] = std::move(sensorObj); 332 333 return std::make_pair(std::move(std::get<sensorLabel>(properties)), 334 std::move(info)); 335 } 336 337 MainLoop::MainLoop(sdbusplus::bus_t&& bus, const std::string& param, 338 const std::string& path, const std::string& devPath, 339 const char* prefix, const char* root, 340 const std::string& instanceId, 341 const hwmonio::HwmonIOInterface* ioIntf) : 342 _bus(std::move(bus)), _manager(_bus, root), _pathParam(param), _hwmonRoot(), 343 _instance(), _devPath(devPath), _prefix(prefix), _root(root), _state(), 344 _instanceId(instanceId), _ioAccess(ioIntf), 345 _event(sdeventplus::Event::get_default()), 346 _timer(_event, std::bind(&MainLoop::read, this)) 347 { 348 // Strip off any trailing slashes. 349 std::string p = path; 350 while (!p.empty() && p.back() == '/') 351 { 352 p.pop_back(); 353 } 354 355 // Given the furthest right /, set instance to 356 // the basename, and hwmonRoot to the leading path. 357 auto n = p.rfind('/'); 358 if (n != std::string::npos) 359 { 360 _instance.assign(p.substr(n + 1)); 361 _hwmonRoot.assign(p.substr(0, n)); 362 } 363 364 assert(!_instance.empty()); 365 assert(!_hwmonRoot.empty()); 366 } 367 368 void MainLoop::shutdown() noexcept 369 { 370 _event.exit(0); 371 } 372 373 void MainLoop::run() 374 { 375 init(); 376 377 std::function<void()> callback(std::bind(&MainLoop::read, this)); 378 try 379 { 380 _timer.restart(std::chrono::microseconds(_interval)); 381 382 // TODO: Issue#6 - Optionally look at polling interval sysfs entry. 383 384 // TODO: Issue#7 - Should probably periodically check the SensorSet 385 // for new entries. 386 387 _bus.attach_event(_event.get(), SD_EVENT_PRIORITY_IMPORTANT); 388 _event.loop(); 389 } 390 catch (const std::exception& e) 391 { 392 log<level::ERR>("Error in sysfs polling loop", 393 entry("ERROR=%s", e.what())); 394 throw; 395 } 396 } 397 398 void MainLoop::init() 399 { 400 // Check sysfs for available sensors. 401 auto sensors = std::make_unique<SensorSet>(_hwmonRoot + '/' + _instance); 402 403 for (const auto& i : *sensors) 404 { 405 auto object = getObject(i); 406 if (object) 407 { 408 // Construct the SensorSet value 409 // std::tuple<SensorSet::mapped_type, 410 // std::string(Sensor Label), 411 // ObjectInfo> 412 auto value = 413 std::make_tuple(std::move(i.second), std::move((*object).first), 414 std::move((*object).second)); 415 416 _state[std::move(i.first)] = std::move(value); 417 } 418 419 // Initialize _averageMap of sensor. e.g. <<power, 1>, <0, 0>> 420 if ((i.first.first == hwmon::type::power) && 421 (phosphor::utility::isAverageEnvSet(i.first))) 422 { 423 _average.setAverageValue(i.first, std::make_pair(0, 0)); 424 } 425 } 426 427 /* If there are no sensors specified by labels, exit. */ 428 if (0 == _state.size()) 429 { 430 exit(0); 431 } 432 433 { 434 std::stringstream ss; 435 std::string id = _instanceId; 436 if (id.empty()) 437 { 438 id = 439 std::to_string(std::hash<std::string>{}(_devPath + _pathParam)); 440 } 441 ss << _prefix << "-" << id << ".Hwmon1"; 442 443 _bus.request_name(ss.str().c_str()); 444 } 445 446 { 447 auto interval = env::getEnv("INTERVAL"); 448 if (!interval.empty()) 449 { 450 _interval = std::strtoull(interval.c_str(), NULL, 10); 451 } 452 } 453 } 454 455 void MainLoop::read() 456 { 457 // TODO: Issue#3 - Need to make calls to the dbus sensor cache here to 458 // ensure the objects all exist? 459 460 // Iterate through all the sensors. 461 for (auto& [sensorSetKey, sensorStateTuple] : _state) 462 { 463 const auto& [sensorSysfsType, sensorSysfsNum] = sensorSetKey; 464 auto& [attrs, unused, objInfo] = sensorStateTuple; 465 466 if (attrs.find(hwmon::entry::input) == attrs.end()) 467 { 468 continue; 469 } 470 471 // Read value from sensor. 472 std::string input = hwmon::entry::input; 473 if (sensorSysfsType == hwmon::type::pwm) 474 { 475 input = ""; 476 } 477 // If type is power and AVERAGE_power* is true in env, use average 478 // instead of input 479 else if ((sensorSysfsType == hwmon::type::power) && 480 (phosphor::utility::isAverageEnvSet(sensorSetKey))) 481 { 482 input = hwmon::entry::average; 483 } 484 485 SensorValueType value; 486 auto& obj = std::get<InterfaceMap>(objInfo); 487 std::unique_ptr<sensor::Sensor>& sensor = _sensorObjects[sensorSetKey]; 488 489 auto& statusIface = std::any_cast<std::shared_ptr<StatusObject>&>( 490 obj[InterfaceType::STATUS]); 491 // As long as addStatus is called before addValue, statusIface 492 // should never be nullptr. 493 assert(statusIface); 494 495 try 496 { 497 if (sensor->hasFaultFile()) 498 { 499 auto fault = _ioAccess->read(sensorSysfsType, sensorSysfsNum, 500 hwmon::entry::fault, 501 hwmonio::retries, hwmonio::delay); 502 // Skip reading from a sensor with a valid fault file 503 // and set the functional property accordingly 504 if (!statusIface->functional((fault == 0) ? true : false)) 505 { 506 continue; 507 } 508 } 509 510 { 511 // RAII object for GPIO unlock / lock 512 auto locker = sensor::gpioUnlock(sensor->getGpio()); 513 514 // For sensors with attribute ASYNC_READ_TIMEOUT, 515 // spawn a thread with timeout 516 auto asyncReadTimeout = 517 env::getEnv("ASYNC_READ_TIMEOUT", sensorSetKey); 518 if (!asyncReadTimeout.empty()) 519 { 520 std::chrono::milliseconds asyncTimeout{ 521 std::stoi(asyncReadTimeout)}; 522 value = sensor::asyncRead( 523 sensorSetKey, _ioAccess, asyncTimeout, _timedoutMap, 524 sensorSysfsType, sensorSysfsNum, input, 525 hwmonio::retries, hwmonio::delay); 526 } 527 else 528 { 529 // Retry for up to a second if device is busy 530 // or has a transient error. 531 value = 532 _ioAccess->read(sensorSysfsType, sensorSysfsNum, input, 533 hwmonio::retries, hwmonio::delay); 534 } 535 536 // Set functional property to true if we could read sensor 537 statusIface->functional(true); 538 539 value = sensor->adjustValue(value); 540 541 if (input == hwmon::entry::average) 542 { 543 // Calculate the values of averageMap based on current 544 // average value, current average_interval value, previous 545 // average value, previous average_interval value 546 int64_t interval = 547 _ioAccess->read(sensorSysfsType, sensorSysfsNum, 548 hwmon::entry::caverage_interval, 549 hwmonio::retries, hwmonio::delay); 550 auto ret = _average.getAverageValue(sensorSetKey); 551 assert(ret); 552 553 const auto& [preAverage, preInterval] = *ret; 554 555 auto calValue = Average::calcAverage( 556 preAverage, preInterval, value, interval); 557 if (calValue) 558 { 559 // Update previous values in averageMap before the 560 // variable value is changed next 561 _average.setAverageValue( 562 sensorSetKey, std::make_pair(value, interval)); 563 // Update value to be calculated average 564 value = calValue.value(); 565 } 566 else 567 { 568 // the value of 569 // power*_average_interval is not changed yet, use the 570 // previous calculated average instead. So skip dbus 571 // update. 572 continue; 573 } 574 } 575 } 576 577 updateSensorInterfaces(obj, value); 578 } 579 catch (const std::system_error& e) 580 { 581 #if UPDATE_FUNCTIONAL_ON_FAIL 582 // If UPDATE_FUNCTIONAL_ON_FAIL is defined and an exception was 583 // thrown, set the functional property to false. 584 // We cannot set this with the 'continue' in the lower block 585 // as the code may exit before reaching it. 586 statusIface->functional(false); 587 #endif 588 auto file = sysfs::make_sysfs_path( 589 _ioAccess->path(), sensorSysfsType, sensorSysfsNum, input); 590 591 // Check sensorAdjusts for sensor removal RCs 592 auto& sAdjusts = _sensorObjects[sensorSetKey]->getAdjusts(); 593 if (sAdjusts.rmRCs.count(e.code().value()) > 0) 594 { 595 // Return code found in sensor return code removal list 596 if (_rmSensors.find(sensorSetKey) == _rmSensors.end()) 597 { 598 // Trace for sensor not already removed from dbus 599 log<level::INFO>("Remove sensor from dbus for read fail", 600 entry("FILE=%s", file.c_str()), 601 entry("RC=%d", e.code().value())); 602 // Mark this sensor to be removed from dbus 603 _rmSensors[sensorSetKey] = attrs; 604 } 605 continue; 606 } 607 #if UPDATE_FUNCTIONAL_ON_FAIL 608 // Do not exit with failure if UPDATE_FUNCTIONAL_ON_FAIL is set 609 continue; 610 #endif 611 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device:: 612 Error; 613 report<ReadFailure>( 614 xyz::openbmc_project::Sensor::Device::ReadFailure:: 615 CALLOUT_ERRNO(e.code().value()), 616 xyz::openbmc_project::Sensor::Device::ReadFailure:: 617 CALLOUT_DEVICE_PATH(_devPath.c_str())); 618 619 log<level::INFO>(std::format("Failing sysfs file: {} errno: {}", 620 file, e.code().value()) 621 .c_str()); 622 623 exit(EXIT_FAILURE); 624 } 625 } 626 627 removeSensors(); 628 629 addDroppedSensors(); 630 } 631 632 void MainLoop::removeSensors() 633 { 634 // Remove any sensors marked for removal 635 for (const auto& i : _rmSensors) 636 { 637 // Remove sensor object from dbus using emit_object_removed() 638 auto& objInfo = std::get<ObjectInfo>(_state[i.first]); 639 auto& objPath = std::get<std::string>(objInfo); 640 641 _bus.emit_object_removed(objPath.c_str()); 642 643 // Erase sensor object info 644 _state.erase(i.first); 645 } 646 } 647 648 void MainLoop::addDroppedSensors() 649 { 650 // Attempt to add any sensors that were removed 651 auto it = _rmSensors.begin(); 652 while (it != _rmSensors.end()) 653 { 654 if (_state.find(it->first) == _state.end()) 655 { 656 SensorSet::container_t::value_type ssValueType = 657 std::make_pair(it->first, it->second); 658 659 auto object = getObject(ssValueType); 660 if (object) 661 { 662 // Construct the SensorSet value 663 // std::tuple<SensorSet::mapped_type, 664 // std::string(Sensor Label), 665 // ObjectInfo> 666 auto value = std::make_tuple(std::move(ssValueType.second), 667 std::move((*object).first), 668 std::move((*object).second)); 669 670 _state[std::move(ssValueType.first)] = std::move(value); 671 672 std::string input = hwmon::entry::input; 673 // If type is power and AVERAGE_power* is true in env, use 674 // average instead of input 675 if ((it->first.first == hwmon::type::power) && 676 (phosphor::utility::isAverageEnvSet(it->first))) 677 { 678 input = hwmon::entry::average; 679 } 680 // Sensor object added, erase entry from removal list 681 auto file = 682 sysfs::make_sysfs_path(_ioAccess->path(), it->first.first, 683 it->first.second, input); 684 685 log<level::INFO>("Added sensor to dbus after successful read", 686 entry("FILE=%s", file.c_str())); 687 688 it = _rmSensors.erase(it); 689 } 690 else 691 { 692 ++it; 693 } 694 } 695 else 696 { 697 // Sanity check to remove sensors that were re-added 698 it = _rmSensors.erase(it); 699 } 700 } 701 } 702 703 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 704