1*6714a25aSJames Feist /* 2*6714a25aSJames Feist // Copyright (c) 2017 Intel Corporation 3*6714a25aSJames Feist // 4*6714a25aSJames Feist // Licensed under the Apache License, Version 2.0 (the "License"); 5*6714a25aSJames Feist // you may not use this file except in compliance with the License. 6*6714a25aSJames Feist // You may obtain a copy of the License at 7*6714a25aSJames Feist // 8*6714a25aSJames Feist // http://www.apache.org/licenses/LICENSE-2.0 9*6714a25aSJames Feist // 10*6714a25aSJames Feist // Unless required by applicable law or agreed to in writing, software 11*6714a25aSJames Feist // distributed under the License is distributed on an "AS IS" BASIS, 12*6714a25aSJames Feist // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*6714a25aSJames Feist // See the License for the specific language governing permissions and 14*6714a25aSJames Feist // limitations under the License. 15*6714a25aSJames Feist */ 16*6714a25aSJames Feist 17*6714a25aSJames Feist #include <HwmonTempSensor.hpp> 18*6714a25aSJames Feist #include <Utils.hpp> 19*6714a25aSJames Feist #include <boost/algorithm/string/predicate.hpp> 20*6714a25aSJames Feist #include <boost/algorithm/string/replace.hpp> 21*6714a25aSJames Feist #include <boost/container/flat_set.hpp> 22*6714a25aSJames Feist #include <experimental/filesystem> 23*6714a25aSJames Feist #include <fstream> 24*6714a25aSJames Feist #include <regex> 25*6714a25aSJames Feist #include <sdbusplus/asio/connection.hpp> 26*6714a25aSJames Feist #include <sdbusplus/asio/object_server.hpp> 27*6714a25aSJames Feist 28*6714a25aSJames Feist static constexpr bool DEBUG = false; 29*6714a25aSJames Feist 30*6714a25aSJames Feist namespace fs = std::experimental::filesystem; 31*6714a25aSJames Feist static constexpr std::array<const char*, 2> SENSOR_TYPES = { 32*6714a25aSJames Feist "xyz.openbmc_project.Configuration.TMP75", 33*6714a25aSJames Feist "xyz.openbmc_project.Configuration.TMP421"}; 34*6714a25aSJames Feist static std::regex INPUT_REGEX(R"(temp(\d+)_input)"); 35*6714a25aSJames Feist 36*6714a25aSJames Feist void createSensors( 37*6714a25aSJames Feist boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer, 38*6714a25aSJames Feist boost::container::flat_map<std::string, std::unique_ptr<HwmonTempSensor>>& 39*6714a25aSJames Feist sensors, 40*6714a25aSJames Feist std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, 41*6714a25aSJames Feist const std::unique_ptr<boost::container::flat_set<std::string>>& 42*6714a25aSJames Feist sensorsChanged) 43*6714a25aSJames Feist { 44*6714a25aSJames Feist bool firstScan = sensorsChanged == nullptr; 45*6714a25aSJames Feist // use new data the first time, then refresh 46*6714a25aSJames Feist ManagedObjectType sensorConfigurations; 47*6714a25aSJames Feist bool useCache = false; 48*6714a25aSJames Feist for (const char* type : SENSOR_TYPES) 49*6714a25aSJames Feist { 50*6714a25aSJames Feist if (!getSensorConfiguration(type, dbusConnection, sensorConfigurations, 51*6714a25aSJames Feist useCache)) 52*6714a25aSJames Feist { 53*6714a25aSJames Feist std::cerr << "error communicating to entity manager\n"; 54*6714a25aSJames Feist return; 55*6714a25aSJames Feist } 56*6714a25aSJames Feist useCache = true; 57*6714a25aSJames Feist } 58*6714a25aSJames Feist std::vector<fs::path> paths; 59*6714a25aSJames Feist if (!find_files(fs::path("/sys/class/hwmon"), R"(temp\d+_input)", paths)) 60*6714a25aSJames Feist { 61*6714a25aSJames Feist std::cerr << "No temperature sensors in system\n"; 62*6714a25aSJames Feist return; 63*6714a25aSJames Feist } 64*6714a25aSJames Feist 65*6714a25aSJames Feist // iterate through all found temp sensors, and try to match them with 66*6714a25aSJames Feist // configuration 67*6714a25aSJames Feist for (auto& path : paths) 68*6714a25aSJames Feist { 69*6714a25aSJames Feist std::smatch match; 70*6714a25aSJames Feist std::string pathStr = path.string(); 71*6714a25aSJames Feist 72*6714a25aSJames Feist std::regex_search(pathStr, match, INPUT_REGEX); 73*6714a25aSJames Feist std::string index = *(match.begin() + 1); 74*6714a25aSJames Feist 75*6714a25aSJames Feist auto directory = path.parent_path(); 76*6714a25aSJames Feist auto oem_name_path = directory.string() + R"(/of_node/oemname)" + index; 77*6714a25aSJames Feist 78*6714a25aSJames Feist if (DEBUG) 79*6714a25aSJames Feist std::cout << "Checking path " << oem_name_path << "\n"; 80*6714a25aSJames Feist std::ifstream nameFile(oem_name_path); 81*6714a25aSJames Feist if (!nameFile.good()) 82*6714a25aSJames Feist { 83*6714a25aSJames Feist std::cerr << "Failure reading " << oem_name_path << "\n"; 84*6714a25aSJames Feist continue; 85*6714a25aSJames Feist } 86*6714a25aSJames Feist std::string oemName; 87*6714a25aSJames Feist std::getline(nameFile, oemName); 88*6714a25aSJames Feist nameFile.close(); 89*6714a25aSJames Feist if (!oemName.size()) 90*6714a25aSJames Feist { 91*6714a25aSJames Feist // shouldn't have an empty name file 92*6714a25aSJames Feist continue; 93*6714a25aSJames Feist } 94*6714a25aSJames Feist oemName.pop_back(); // remove trailing null 95*6714a25aSJames Feist 96*6714a25aSJames Feist const SensorData* sensorData = nullptr; 97*6714a25aSJames Feist const std::string* interfacePath = nullptr; 98*6714a25aSJames Feist for (const std::pair<sdbusplus::message::object_path, SensorData>& 99*6714a25aSJames Feist sensor : sensorConfigurations) 100*6714a25aSJames Feist { 101*6714a25aSJames Feist if (!boost::ends_with(sensor.first.str, oemName)) 102*6714a25aSJames Feist { 103*6714a25aSJames Feist continue; 104*6714a25aSJames Feist } 105*6714a25aSJames Feist sensorData = &(sensor.second); 106*6714a25aSJames Feist interfacePath = &(sensor.first.str); 107*6714a25aSJames Feist break; 108*6714a25aSJames Feist } 109*6714a25aSJames Feist if (sensorData == nullptr) 110*6714a25aSJames Feist { 111*6714a25aSJames Feist std::cerr << "failed to find match for " << oemName << "\n"; 112*6714a25aSJames Feist continue; 113*6714a25aSJames Feist } 114*6714a25aSJames Feist const std::pair<std::string, boost::container::flat_map< 115*6714a25aSJames Feist std::string, BasicVariantType>>* 116*6714a25aSJames Feist baseConfiguration = nullptr; 117*6714a25aSJames Feist const char* sensorType = nullptr; 118*6714a25aSJames Feist for (const char* type : SENSOR_TYPES) 119*6714a25aSJames Feist { 120*6714a25aSJames Feist auto sensorBase = sensorData->find(type); 121*6714a25aSJames Feist if (sensorBase != sensorData->end()) 122*6714a25aSJames Feist { 123*6714a25aSJames Feist baseConfiguration = &(*sensorBase); 124*6714a25aSJames Feist sensorType = type; 125*6714a25aSJames Feist break; 126*6714a25aSJames Feist } 127*6714a25aSJames Feist } 128*6714a25aSJames Feist 129*6714a25aSJames Feist if (baseConfiguration == nullptr) 130*6714a25aSJames Feist { 131*6714a25aSJames Feist std::cerr << "error finding base configuration for" << oemName 132*6714a25aSJames Feist << "\n"; 133*6714a25aSJames Feist continue; 134*6714a25aSJames Feist } 135*6714a25aSJames Feist 136*6714a25aSJames Feist auto findSensorName = baseConfiguration->second.find("Name"); 137*6714a25aSJames Feist if (findSensorName == baseConfiguration->second.end()) 138*6714a25aSJames Feist { 139*6714a25aSJames Feist std::cerr << "could not determine configuration name for " 140*6714a25aSJames Feist << oemName << "\n"; 141*6714a25aSJames Feist continue; 142*6714a25aSJames Feist } 143*6714a25aSJames Feist std::string sensorName = 144*6714a25aSJames Feist sdbusplus::message::variant_ns::get<std::string>( 145*6714a25aSJames Feist findSensorName->second); 146*6714a25aSJames Feist // on rescans, only update sensors we were signaled by 147*6714a25aSJames Feist auto findSensor = sensors.find(sensorName); 148*6714a25aSJames Feist if (!firstScan && findSensor != sensors.end()) 149*6714a25aSJames Feist { 150*6714a25aSJames Feist bool found = false; 151*6714a25aSJames Feist for (auto it = sensorsChanged->begin(); it != sensorsChanged->end(); 152*6714a25aSJames Feist it++) 153*6714a25aSJames Feist { 154*6714a25aSJames Feist if (boost::ends_with(*it, findSensor->second->name)) 155*6714a25aSJames Feist { 156*6714a25aSJames Feist sensorsChanged->erase(it); 157*6714a25aSJames Feist findSensor->second = nullptr; 158*6714a25aSJames Feist found = true; 159*6714a25aSJames Feist break; 160*6714a25aSJames Feist } 161*6714a25aSJames Feist } 162*6714a25aSJames Feist if (!found) 163*6714a25aSJames Feist { 164*6714a25aSJames Feist continue; 165*6714a25aSJames Feist } 166*6714a25aSJames Feist } 167*6714a25aSJames Feist std::vector<thresholds::Threshold> sensorThresholds; 168*6714a25aSJames Feist if (!ParseThresholdsFromConfig(*sensorData, sensorThresholds)) 169*6714a25aSJames Feist { 170*6714a25aSJames Feist std::cerr << "error populating thresholds for " << sensorName 171*6714a25aSJames Feist << "\n"; 172*6714a25aSJames Feist } 173*6714a25aSJames Feist 174*6714a25aSJames Feist sensors[sensorName] = std::make_unique<HwmonTempSensor>( 175*6714a25aSJames Feist path.string(), sensorType, objectServer, dbusConnection, io, 176*6714a25aSJames Feist sensorName, std::move(sensorThresholds), *interfacePath); 177*6714a25aSJames Feist } 178*6714a25aSJames Feist } 179*6714a25aSJames Feist 180*6714a25aSJames Feist int main(int argc, char** argv) 181*6714a25aSJames Feist { 182*6714a25aSJames Feist boost::asio::io_service io; 183*6714a25aSJames Feist auto systemBus = std::make_shared<sdbusplus::asio::connection>(io); 184*6714a25aSJames Feist systemBus->request_name("xyz.openbmc_project.HwmonTempSensor"); 185*6714a25aSJames Feist sdbusplus::asio::object_server objectServer(systemBus); 186*6714a25aSJames Feist boost::container::flat_map<std::string, std::unique_ptr<HwmonTempSensor>> 187*6714a25aSJames Feist sensors; 188*6714a25aSJames Feist std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches; 189*6714a25aSJames Feist std::unique_ptr<boost::container::flat_set<std::string>> sensorsChanged = 190*6714a25aSJames Feist std::make_unique<boost::container::flat_set<std::string>>(); 191*6714a25aSJames Feist 192*6714a25aSJames Feist io.post([&]() { 193*6714a25aSJames Feist createSensors(io, objectServer, sensors, systemBus, nullptr); 194*6714a25aSJames Feist }); 195*6714a25aSJames Feist 196*6714a25aSJames Feist boost::asio::deadline_timer filterTimer(io); 197*6714a25aSJames Feist std::function<void(sdbusplus::message::message&)> eventHandler = 198*6714a25aSJames Feist [&](sdbusplus::message::message& message) { 199*6714a25aSJames Feist if (message.is_method_error()) 200*6714a25aSJames Feist { 201*6714a25aSJames Feist std::cerr << "callback method error\n"; 202*6714a25aSJames Feist return; 203*6714a25aSJames Feist } 204*6714a25aSJames Feist sensorsChanged->insert(message.get_path()); 205*6714a25aSJames Feist // this implicitly cancels the timer 206*6714a25aSJames Feist filterTimer.expires_from_now(boost::posix_time::seconds(1)); 207*6714a25aSJames Feist 208*6714a25aSJames Feist filterTimer.async_wait([&](const boost::system::error_code& ec) { 209*6714a25aSJames Feist if (ec == boost::asio::error::operation_aborted) 210*6714a25aSJames Feist { 211*6714a25aSJames Feist /* we were canceled*/ 212*6714a25aSJames Feist return; 213*6714a25aSJames Feist } 214*6714a25aSJames Feist else if (ec) 215*6714a25aSJames Feist { 216*6714a25aSJames Feist std::cerr << "timer error\n"; 217*6714a25aSJames Feist return; 218*6714a25aSJames Feist } 219*6714a25aSJames Feist createSensors(io, objectServer, sensors, systemBus, 220*6714a25aSJames Feist sensorsChanged); 221*6714a25aSJames Feist }); 222*6714a25aSJames Feist }; 223*6714a25aSJames Feist 224*6714a25aSJames Feist for (const char* type : SENSOR_TYPES) 225*6714a25aSJames Feist { 226*6714a25aSJames Feist auto match = std::make_unique<sdbusplus::bus::match::match>( 227*6714a25aSJames Feist static_cast<sdbusplus::bus::bus&>(*systemBus), 228*6714a25aSJames Feist "type='signal',member='PropertiesChanged',path_namespace='" + 229*6714a25aSJames Feist std::string(INVENTORY_PATH) + "',arg0namespace='" + type + "'", 230*6714a25aSJames Feist eventHandler); 231*6714a25aSJames Feist matches.emplace_back(std::move(match)); 232*6714a25aSJames Feist } 233*6714a25aSJames Feist 234*6714a25aSJames Feist io.run(); 235*6714a25aSJames Feist } 236