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