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