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 <iostream> 17 #include <memory> 18 #include <cstdlib> 19 #include <algorithm> 20 #include "sensorset.hpp" 21 #include "hwmon.hpp" 22 #include "sysfs.hpp" 23 #include "mainloop.hpp" 24 #include "env.hpp" 25 #include "thresholds.hpp" 26 #include "targets.hpp" 27 #include "fan_speed.hpp" 28 29 // Initialization for Warning Objects 30 decltype(Thresholds<WarningObject>::setLo) Thresholds<WarningObject>::setLo = 31 &WarningObject::warningLow; 32 decltype(Thresholds<WarningObject>::setHi) Thresholds<WarningObject>::setHi = 33 &WarningObject::warningHigh; 34 decltype(Thresholds<WarningObject>::getLo) Thresholds<WarningObject>::getLo = 35 &WarningObject::warningLow; 36 decltype(Thresholds<WarningObject>::getHi) Thresholds<WarningObject>::getHi = 37 &WarningObject::warningHigh; 38 decltype(Thresholds<WarningObject>::alarmLo) Thresholds<WarningObject>::alarmLo = 39 &WarningObject::warningAlarmLow; 40 decltype(Thresholds<WarningObject>::alarmHi) Thresholds<WarningObject>::alarmHi = 41 &WarningObject::warningAlarmHigh; 42 43 // Initialization for Critical Objects 44 decltype(Thresholds<CriticalObject>::setLo) Thresholds<CriticalObject>::setLo = 45 &CriticalObject::criticalLow; 46 decltype(Thresholds<CriticalObject>::setHi) Thresholds<CriticalObject>::setHi = 47 &CriticalObject::criticalHigh; 48 decltype(Thresholds<CriticalObject>::getLo) Thresholds<CriticalObject>::getLo = 49 &CriticalObject::criticalLow; 50 decltype(Thresholds<CriticalObject>::getHi) Thresholds<CriticalObject>::getHi = 51 &CriticalObject::criticalHigh; 52 decltype(Thresholds<CriticalObject>::alarmLo) Thresholds<CriticalObject>::alarmLo = 53 &CriticalObject::criticalAlarmLow; 54 decltype(Thresholds<CriticalObject>::alarmHi) Thresholds<CriticalObject>::alarmHi = 55 &CriticalObject::criticalAlarmHigh; 56 57 58 59 static constexpr auto typeAttrMap = 60 { 61 // 1 - hwmon class 62 // 2 - unit 63 // 3 - sysfs scaling factor 64 std::make_tuple( 65 hwmon::type::ctemp, 66 ValueInterface::Unit::DegreesC, 67 -3, 68 "temperature"), 69 std::make_tuple( 70 hwmon::type::cfan, 71 ValueInterface::Unit::RPMS, 72 0, 73 "fan_tach"), 74 std::make_tuple( 75 hwmon::type::cvolt, 76 ValueInterface::Unit::Volts, 77 -3, 78 "voltage"), 79 std::make_tuple( 80 hwmon::type::ccurr, 81 ValueInterface::Unit::Amperes, 82 -3, 83 "current"), 84 std::make_tuple( 85 hwmon::type::cenergy, 86 ValueInterface::Unit::Joules, 87 -6, 88 "energy"), 89 std::make_tuple( 90 hwmon::type::cpower, 91 ValueInterface::Unit::Watts, 92 -6, 93 "power"), 94 }; 95 96 auto getHwmonType(decltype(typeAttrMap)::const_reference attrs) 97 { 98 return std::get<0>(attrs); 99 } 100 101 auto getUnit(decltype(typeAttrMap)::const_reference attrs) 102 { 103 return std::get<1>(attrs); 104 } 105 106 auto getScale(decltype(typeAttrMap)::const_reference attrs) 107 { 108 return std::get<2>(attrs); 109 } 110 111 auto getNamespace(decltype(typeAttrMap)::const_reference attrs) 112 { 113 return std::get<3>(attrs); 114 } 115 116 using AttributeIterator = decltype(*typeAttrMap.begin()); 117 using Attributes 118 = std::remove_cv<std::remove_reference<AttributeIterator>::type>::type; 119 120 auto getAttributes(const std::string& type, Attributes& attributes) 121 { 122 // *INDENT-OFF* 123 auto a = std::find_if( 124 typeAttrMap.begin(), 125 typeAttrMap.end(), 126 [&](const auto & e) 127 { 128 return type == getHwmonType(e); 129 }); 130 // *INDENT-ON* 131 132 if (a == typeAttrMap.end()) 133 { 134 return false; 135 } 136 137 attributes = *a; 138 return true; 139 } 140 141 auto addValue(const SensorSet::key_type& sensor, 142 const std::string& hwmonRoot, 143 const std::string& instance, 144 ObjectInfo& info) 145 { 146 static constexpr bool deferSignals = true; 147 148 // Get the initial value for the value interface. 149 auto& bus = *std::get<sdbusplus::bus::bus*>(info); 150 auto& obj = std::get<Object>(info); 151 auto& objPath = std::get<std::string>(info); 152 153 int val = readSysfsWithCallout(hwmonRoot, 154 instance, 155 sensor.first, 156 sensor.second, 157 hwmon::entry::input); 158 auto iface = std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals); 159 iface->value(val); 160 161 Attributes attrs; 162 if (getAttributes(sensor.first, attrs)) 163 { 164 iface->unit(getUnit(attrs)); 165 iface->scale(getScale(attrs)); 166 } 167 168 obj[InterfaceType::VALUE] = iface; 169 return iface; 170 } 171 172 MainLoop::MainLoop( 173 sdbusplus::bus::bus&& bus, 174 const std::string& path, 175 const char* prefix, 176 const char* root) 177 : _bus(std::move(bus)), 178 _manager(_bus, root), 179 _shutdown(false), 180 _hwmonRoot(), 181 _instance(), 182 _prefix(prefix), 183 _root(root), 184 state() 185 { 186 std::string p = path; 187 while (!p.empty() && p.back() == '/') 188 { 189 p.pop_back(); 190 } 191 192 auto n = p.rfind('/'); 193 if (n != std::string::npos) 194 { 195 _instance.assign(p.substr(n + 1)); 196 _hwmonRoot.assign(p.substr(0, n)); 197 } 198 199 assert(!_instance.empty()); 200 assert(!_hwmonRoot.empty()); 201 } 202 203 void MainLoop::shutdown() noexcept 204 { 205 _shutdown = true; 206 } 207 208 void MainLoop::run() 209 { 210 // Check sysfs for available sensors. 211 auto sensors = std::make_unique<SensorSet>(_hwmonRoot + '/' + _instance); 212 213 for (auto& i : *sensors) 214 { 215 std::string label; 216 217 /* 218 * Check if the value of the MODE_<item><X> env variable for the sensor 219 * is "label", then read the sensor number from the <item><X>_label 220 * file. The name of the DBUS object would be the value of the env 221 * variable LABEL_<item><sensorNum>. If the MODE_<item><X> env variable 222 * does'nt exist, then the name of DBUS object is the value of the env 223 * variable LABEL_<item><X>. 224 */ 225 auto mode = getEnv("MODE", i.first); 226 if (!mode.compare(hwmon::entry::label)) 227 { 228 label = getIndirectLabelEnv( 229 "LABEL", _hwmonRoot + '/' + _instance + '/', i.first); 230 if (label.empty()) 231 { 232 continue; 233 } 234 } 235 else 236 { 237 // Ignore inputs without a label. 238 label = getEnv("LABEL", i.first); 239 if (label.empty()) 240 { 241 continue; 242 } 243 } 244 245 Attributes attrs; 246 if (!getAttributes(i.first.first, attrs)) 247 { 248 continue; 249 } 250 251 std::string objectPath{_root}; 252 objectPath.append(1, '/'); 253 objectPath.append(getNamespace(attrs)); 254 objectPath.append(1, '/'); 255 objectPath.append(label); 256 257 ObjectInfo info(&_bus, std::move(objectPath), Object()); 258 auto valueInterface = addValue(i.first, _hwmonRoot, _instance, info); 259 auto sensorValue = valueInterface->value(); 260 addThreshold<WarningObject>(i.first, sensorValue, info); 261 addThreshold<CriticalObject>(i.first, sensorValue, info); 262 //TODO openbmc/openbmc#1347 263 // Handle application restarts to set/refresh fan speed values 264 auto target = addTarget<hwmon::FanSpeed>( 265 i.first, _hwmonRoot, _instance, info); 266 267 if (target) 268 { 269 target->enable(); 270 } 271 272 // All the interfaces have been created. Go ahead 273 // and emit InterfacesAdded. 274 valueInterface->emit_object_added(); 275 276 auto value = std::make_tuple( 277 std::move(i.second), 278 std::move(label), 279 std::move(info)); 280 281 state[std::move(i.first)] = std::move(value); 282 } 283 284 /* If there are no sensors specified by labels, exit. */ 285 if (0 == state.size()) 286 { 287 return; 288 } 289 290 { 291 std::string busname{_prefix}; 292 busname.append(1, '.'); 293 busname.append(_instance); 294 _bus.request_name(busname.c_str()); 295 } 296 297 { 298 auto interval = getenv("INTERVAL"); 299 if (interval) 300 { 301 _interval = strtoull(interval, NULL, 10); 302 } 303 } 304 305 // TODO: Issue#3 - Need to make calls to the dbus sensor cache here to 306 // ensure the objects all exist? 307 308 // Polling loop. 309 while (!_shutdown) 310 { 311 // Iterate through all the sensors. 312 for (auto& i : state) 313 { 314 auto& attrs = std::get<0>(i.second); 315 if (attrs.find(hwmon::entry::input) != attrs.end()) 316 { 317 // Read value from sensor. 318 int value = readSysfsWithCallout(_hwmonRoot, 319 _instance, 320 i.first.first, 321 i.first.second, 322 hwmon::entry::input); 323 auto& objInfo = std::get<ObjectInfo>(i.second); 324 auto& obj = std::get<Object>(objInfo); 325 326 for (auto& iface : obj) 327 { 328 auto valueIface = std::shared_ptr<ValueObject>(); 329 auto warnIface = std::shared_ptr<WarningObject>(); 330 auto critIface = std::shared_ptr<CriticalObject>(); 331 332 switch (iface.first) 333 { 334 case InterfaceType::VALUE: 335 valueIface = std::experimental::any_cast<std::shared_ptr<ValueObject>> 336 (iface.second); 337 valueIface->value(value); 338 break; 339 case InterfaceType::WARN: 340 checkThresholds<WarningObject>(iface.second, value); 341 break; 342 case InterfaceType::CRIT: 343 checkThresholds<CriticalObject>(iface.second, value); 344 break; 345 default: 346 break; 347 } 348 } 349 } 350 } 351 352 // Respond to DBus 353 _bus.process_discard(); 354 355 // Sleep until next interval. 356 // TODO: Issue#6 - Optionally look at polling interval sysfs entry. 357 _bus.wait(_interval); 358 359 // TODO: Issue#7 - Should probably periodically check the SensorSet 360 // for new entries. 361 } 362 } 363 364 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 365