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