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& sysfsRoot, ObjectInfo& info) 114 { 115 static constexpr bool deferSignals = true; 116 117 // Get the initial value for the value interface. 118 auto& bus = *std::get<sdbusplus::bus::bus*>(info); 119 auto& obj = std::get<Object>(info); 120 auto& objPath = std::get<std::string>(info); 121 122 auto sysfsPath = make_sysfs_path( 123 sysfsRoot, 124 sensor.first, 125 sensor.second, 126 hwmon::entry::input); 127 int val = 0; 128 read_sysfs(sysfsPath, val); 129 130 auto iface = std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals); 131 iface->value(val); 132 133 Attributes attrs; 134 if (getAttributes(sensor.first, attrs)) 135 { 136 iface->unit(getUnit(attrs)); 137 iface->scale(getScale(attrs)); 138 } 139 140 obj[InterfaceType::VALUE] = iface; 141 return iface; 142 } 143 144 MainLoop::MainLoop( 145 sdbusplus::bus::bus&& bus, 146 const std::string& path, 147 const char* prefix, 148 const char* root) 149 : _bus(std::move(bus)), 150 _manager(sdbusplus::server::manager::manager(_bus, root)), 151 _shutdown(false), 152 _hwmonRoot(), 153 _instance(), 154 _prefix(prefix), 155 _root(root), 156 state() 157 { 158 std::string p = path; 159 while (!p.empty() && p.back() == '/') 160 { 161 p.pop_back(); 162 } 163 164 auto n = p.rfind('/'); 165 if (n != std::string::npos) 166 { 167 _instance.assign(p.substr(n + 1)); 168 _hwmonRoot.assign(p.substr(0, n)); 169 } 170 171 assert(!_instance.empty()); 172 assert(!_hwmonRoot.empty()); 173 } 174 175 void MainLoop::shutdown() noexcept 176 { 177 _shutdown = true; 178 } 179 180 void MainLoop::run() 181 { 182 // Check sysfs for available sensors. 183 std::string hwmonPath = _hwmonRoot + '/' + _instance; 184 auto sensors = std::make_unique<SensorSet>(hwmonPath); 185 186 for (auto& i : *sensors) 187 { 188 // Get sensor configuration from the environment. 189 190 // Ignore inputs without a label. 191 auto label = getEnv("LABEL", i.first); 192 if (label.empty()) 193 { 194 continue; 195 } 196 197 Attributes attrs; 198 if (!getAttributes(i.first.first, attrs)) 199 { 200 continue; 201 } 202 203 std::string objectPath{_root}; 204 objectPath.append(1, '/'); 205 objectPath.append(getNamespace(attrs)); 206 objectPath.append(1, '/'); 207 objectPath.append(label); 208 209 ObjectInfo info(&_bus, std::move(objectPath), Object()); 210 auto valueInterface = addValue(i.first, hwmonPath, info); 211 auto sensorValue = valueInterface->value(); 212 addThreshold<WarningObject>(i.first, sensorValue, info); 213 addThreshold<CriticalObject>(i.first, sensorValue, info); 214 215 // All the interfaces have been created. Go ahead 216 // and emit InterfacesAdded. 217 valueInterface->emit_object_added(); 218 219 auto value = std::make_tuple( 220 std::move(i.second), 221 std::move(label), 222 std::move(info)); 223 224 state[std::move(i.first)] = std::move(value); 225 } 226 227 { 228 std::string busname{_prefix}; 229 busname.append(1, '.'); 230 busname.append(_instance); 231 _bus.request_name(busname.c_str()); 232 } 233 234 // TODO: Issue#3 - Need to make calls to the dbus sensor cache here to 235 // ensure the objects all exist? 236 237 // Polling loop. 238 while (!_shutdown) 239 { 240 // Iterate through all the sensors. 241 for (auto& i : state) 242 { 243 auto& attrs = std::get<0>(i.second); 244 if (attrs.find(hwmon::entry::input) != attrs.end()) 245 { 246 // Read value from sensor. 247 int value = 0; 248 read_sysfs(make_sysfs_path(hwmonPath, 249 i.first.first, i.first.second, 250 hwmon::entry::input), 251 value); 252 253 auto& objInfo = std::get<ObjectInfo>(i.second); 254 auto& obj = std::get<Object>(objInfo); 255 256 for (auto& iface : obj) 257 { 258 auto valueIface = std::shared_ptr<ValueObject>(); 259 auto warnIface = std::shared_ptr<WarningObject>(); 260 auto critIface = std::shared_ptr<CriticalObject>(); 261 262 switch (iface.first) 263 { 264 case InterfaceType::VALUE: 265 valueIface = std::experimental::any_cast<std::shared_ptr<ValueObject>> 266 (iface.second); 267 valueIface->value(value); 268 break; 269 case InterfaceType::WARN: 270 checkThresholds<WarningObject>(iface.second, value); 271 break; 272 case InterfaceType::CRIT: 273 checkThresholds<CriticalObject>(iface.second, value); 274 break; 275 default: 276 break; 277 } 278 } 279 } 280 } 281 282 // Respond to DBus 283 _bus.process_discard(); 284 285 // Sleep until next interval. 286 // TODO: Issue#5 - Make this configurable. 287 // TODO: Issue#6 - Optionally look at polling interval sysfs entry. 288 _bus.wait((1000000us).count()); 289 290 // TODO: Issue#7 - Should probably periodically check the SensorSet 291 // for new entries. 292 } 293 } 294 295 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 296