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 <chrono> 20 #include <algorithm> 21 #include "sensorset.hpp" 22 #include "hwmon.hpp" 23 #include "sysfs.hpp" 24 #include "mainloop.hpp" 25 #include "env.hpp" 26 #include "thresholds.hpp" 27 28 using namespace std::literals::chrono_literals; 29 30 static constexpr auto typeAttrMap = 31 { 32 // 1 - hwmon class 33 // 2 - unit 34 // 3 - sysfs scaling factor 35 std::make_tuple( 36 hwmon::type::ctemp, 37 ValueInterface::Unit::DegreesC, 38 -3, 39 "temperature"), 40 std::make_tuple( 41 hwmon::type::cfan, 42 ValueInterface::Unit::RPMS, 43 0, 44 "fan_tach"), 45 std::make_tuple( 46 hwmon::type::cvolt, 47 ValueInterface::Unit::Volts, 48 -3, 49 "voltage"), 50 std::make_tuple( 51 hwmon::type::ccurr, 52 ValueInterface::Unit::Amperes, 53 -3, 54 "current"), 55 std::make_tuple( 56 hwmon::type::cenergy, 57 ValueInterface::Unit::Joules, 58 -6, 59 "energy"), 60 std::make_tuple( 61 hwmon::type::cpower, 62 ValueInterface::Unit::Watts, 63 -6, 64 "power"), 65 }; 66 67 auto getHwmonType(decltype(typeAttrMap)::const_reference attrs) 68 { 69 return std::get<0>(attrs); 70 } 71 72 auto getUnit(decltype(typeAttrMap)::const_reference attrs) 73 { 74 return std::get<1>(attrs); 75 } 76 77 auto getScale(decltype(typeAttrMap)::const_reference attrs) 78 { 79 return std::get<2>(attrs); 80 } 81 82 auto getNamespace(decltype(typeAttrMap)::const_reference attrs) 83 { 84 return std::get<3>(attrs); 85 } 86 87 using AttributeIterator = decltype(*typeAttrMap.begin()); 88 using Attributes 89 = std::remove_cv<std::remove_reference<AttributeIterator>::type>::type; 90 91 auto getAttributes(const std::string& type, Attributes& attributes) 92 { 93 // *INDENT-OFF* 94 auto a = std::find_if( 95 typeAttrMap.begin(), 96 typeAttrMap.end(), 97 [&](const auto & e) 98 { 99 return type == getHwmonType(e); 100 }); 101 // *INDENT-ON* 102 103 if (a == typeAttrMap.end()) 104 { 105 return false; 106 } 107 108 attributes = *a; 109 return true; 110 } 111 112 auto addValue(const SensorSet::key_type& sensor, 113 const std::string& hwmonRoot, 114 const std::string& instance, 115 ObjectInfo& info) 116 { 117 static constexpr bool deferSignals = true; 118 119 // Get the initial value for the value interface. 120 auto& bus = *std::get<sdbusplus::bus::bus*>(info); 121 auto& obj = std::get<Object>(info); 122 auto& objPath = std::get<std::string>(info); 123 124 int val = readSysfsWithCallout(hwmonRoot, 125 instance, 126 sensor.first, 127 sensor.second, 128 hwmon::entry::input); 129 auto iface = std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals); 130 iface->value(val); 131 132 Attributes attrs; 133 if (getAttributes(sensor.first, attrs)) 134 { 135 iface->unit(getUnit(attrs)); 136 iface->scale(getScale(attrs)); 137 } 138 139 obj[InterfaceType::VALUE] = iface; 140 return iface; 141 } 142 143 MainLoop::MainLoop( 144 sdbusplus::bus::bus&& bus, 145 const std::string& path, 146 const char* prefix, 147 const char* root) 148 : _bus(std::move(bus)), 149 _manager(sdbusplus::server::manager::manager(_bus, root)), 150 _shutdown(false), 151 _hwmonRoot(), 152 _instance(), 153 _prefix(prefix), 154 _root(root), 155 state() 156 { 157 std::string p = path; 158 while (!p.empty() && p.back() == '/') 159 { 160 p.pop_back(); 161 } 162 163 auto n = p.rfind('/'); 164 if (n != std::string::npos) 165 { 166 _instance.assign(p.substr(n + 1)); 167 _hwmonRoot.assign(p.substr(0, n)); 168 } 169 170 assert(!_instance.empty()); 171 assert(!_hwmonRoot.empty()); 172 } 173 174 void MainLoop::shutdown() noexcept 175 { 176 _shutdown = true; 177 } 178 179 void MainLoop::run() 180 { 181 // Check sysfs for available sensors. 182 auto sensors = std::make_unique<SensorSet>(_hwmonRoot + '/' + _instance); 183 184 for (auto& i : *sensors) 185 { 186 // Get sensor configuration from the environment. 187 188 // Ignore inputs without a label. 189 auto label = getEnv("LABEL", i.first); 190 if (label.empty()) 191 { 192 continue; 193 } 194 195 Attributes attrs; 196 if (!getAttributes(i.first.first, attrs)) 197 { 198 continue; 199 } 200 201 std::string objectPath{_root}; 202 objectPath.append(1, '/'); 203 objectPath.append(getNamespace(attrs)); 204 objectPath.append(1, '/'); 205 objectPath.append(label); 206 207 ObjectInfo info(&_bus, std::move(objectPath), Object()); 208 auto valueInterface = addValue(i.first, _hwmonRoot, _instance, info); 209 auto sensorValue = valueInterface->value(); 210 addThreshold<WarningObject>(i.first, sensorValue, info); 211 addThreshold<CriticalObject>(i.first, sensorValue, info); 212 213 // All the interfaces have been created. Go ahead 214 // and emit InterfacesAdded. 215 valueInterface->emit_object_added(); 216 217 auto value = std::make_tuple( 218 std::move(i.second), 219 std::move(label), 220 std::move(info)); 221 222 state[std::move(i.first)] = std::move(value); 223 } 224 225 { 226 std::string busname{_prefix}; 227 busname.append(1, '.'); 228 busname.append(_instance); 229 _bus.request_name(busname.c_str()); 230 } 231 232 // TODO: Issue#3 - Need to make calls to the dbus sensor cache here to 233 // ensure the objects all exist? 234 235 // Polling loop. 236 while (!_shutdown) 237 { 238 // Iterate through all the sensors. 239 for (auto& i : state) 240 { 241 auto& attrs = std::get<0>(i.second); 242 if (attrs.find(hwmon::entry::input) != attrs.end()) 243 { 244 // Read value from sensor. 245 int value = readSysfsWithCallout(_hwmonRoot, 246 _instance, 247 i.first.first, 248 i.first.second, 249 hwmon::entry::input); 250 auto& objInfo = std::get<ObjectInfo>(i.second); 251 auto& obj = std::get<Object>(objInfo); 252 253 for (auto& iface : obj) 254 { 255 auto valueIface = std::shared_ptr<ValueObject>(); 256 auto warnIface = std::shared_ptr<WarningObject>(); 257 auto critIface = std::shared_ptr<CriticalObject>(); 258 259 switch (iface.first) 260 { 261 case InterfaceType::VALUE: 262 valueIface = std::experimental::any_cast<std::shared_ptr<ValueObject>> 263 (iface.second); 264 valueIface->value(value); 265 break; 266 case InterfaceType::WARN: 267 checkThresholds<WarningObject>(iface.second, value); 268 break; 269 case InterfaceType::CRIT: 270 checkThresholds<CriticalObject>(iface.second, value); 271 break; 272 default: 273 break; 274 } 275 } 276 } 277 } 278 279 // Respond to DBus 280 _bus.process_discard(); 281 282 // Sleep until next interval. 283 // TODO: Issue#5 - Make this configurable. 284 // TODO: Issue#6 - Optionally look at polling interval sysfs entry. 285 _bus.wait((1000000us).count()); 286 287 // TODO: Issue#7 - Should probably periodically check the SensorSet 288 // for new entries. 289 } 290 } 291 292 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 293