blob: 6818692a0756ac76e6473e0cbd8cd64856e189bd [file] [log] [blame]
James Feist139cb572018-09-10 15:26:18 -07001/*
2// Copyright (c) 2017 Intel 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
17#include <PwmSensor.hpp>
18#include <TachSensor.hpp>
19#include <Utils.hpp>
20#include <VariantVisitors.hpp>
21#include <boost/algorithm/string/predicate.hpp>
22#include <boost/algorithm/string/replace.hpp>
23#include <boost/container/flat_set.hpp>
24#include <boost/lexical_cast.hpp>
25#include <experimental/filesystem>
26#include <fstream>
27#include <regex>
28#include <sdbusplus/asio/connection.hpp>
29#include <sdbusplus/asio/object_server.hpp>
30
31static constexpr bool DEBUG = false;
32
33namespace fs = std::experimental::filesystem;
34static constexpr std::array<const char*, 1> SENSOR_TYPES = {
35 "xyz.openbmc_project.Configuration.AspeedFan"};
36static std::regex INPUT_REGEX(R"(fan(\d+)_input)");
37
38void createSensors(
39 boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
40 boost::container::flat_map<std::string, std::unique_ptr<TachSensor>>&
41 tachSensors,
42 boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>&
43 pwmSensors,
44 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
45 const std::unique_ptr<boost::container::flat_set<std::string>>&
46 sensorsChanged)
47{
48 bool firstScan = sensorsChanged == nullptr;
49 // use new data the first time, then refresh
50 ManagedObjectType sensorConfigurations;
51 bool useCache = false;
52 for (const char* type : SENSOR_TYPES)
53 {
54 if (!getSensorConfiguration(type, dbusConnection, sensorConfigurations,
55 useCache))
56 {
57 std::cerr << "error communicating to entity manager\n";
58 return;
59 }
60 useCache = true;
61 }
62 std::vector<fs::path> paths;
63 if (!find_files(fs::path("/sys/class/hwmon"), R"(fan\d+_input)", paths))
64 {
65 std::cerr << "No temperature sensors in system\n";
66 return;
67 }
68
69 // iterate through all found fan sensors, and try to match them with
70 // configuration
71 for (auto& path : paths)
72 {
73 std::smatch match;
74 std::string pathStr = path.string();
75
76 std::regex_search(pathStr, match, INPUT_REGEX);
77 std::string indexStr = *(match.begin() + 1);
78
79 auto directory = path.parent_path();
80 // convert to 0 based
81 size_t index = std::stoul(indexStr) - 1;
82
83 const char* baseType;
84 const SensorData* sensorData = nullptr;
85 const std::string* interfacePath = nullptr;
86 const std::pair<std::string, boost::container::flat_map<
87 std::string, BasicVariantType>>*
88 baseConfiguration = nullptr;
89 for (const std::pair<sdbusplus::message::object_path, SensorData>&
90 sensor : sensorConfigurations)
91 {
92 // find the base of the configuration to see if indexes match
93 for (const char* type : SENSOR_TYPES)
94 {
95 auto sensorBaseFind = sensor.second.find(type);
96 if (sensorBaseFind != sensor.second.end())
97 {
98 baseConfiguration = &(*sensorBaseFind);
99 interfacePath = &(sensor.first.str);
100 baseType = type;
101 break;
102 }
103 }
104 if (baseConfiguration == nullptr)
105 {
106 continue;
107 }
108 auto connector =
109 sensor.second.find(baseType + std::string(".Connector"));
110 if (connector == sensor.second.end())
111 {
112 std::cerr << baseConfiguration->first << " missing connector\n";
113 continue;
114 }
115 auto findPwmIndex = connector->second.find("Pwm");
116 if (findPwmIndex == connector->second.end())
117 {
118 continue;
119 }
120 uint16_t pwmIndex = mapbox::util::apply_visitor(
121 VariantToUnsignedIntVisitor(), findPwmIndex->second);
122 auto oemNamePath = directory.string() + R"(/of_node/oemname)" +
123 std::to_string(pwmIndex);
124
125 if (DEBUG)
126 std::cout << "Checking path " << oemNamePath << "\n";
127 std::ifstream nameFile(oemNamePath);
128 if (!nameFile.good())
129 {
130 continue;
131 }
132 std::string oemName;
133 std::getline(nameFile, oemName);
134 nameFile.close();
135 if (!oemName.size())
136 {
137 // shouldn't have an empty name file
138 continue;
139 }
140 oemName.pop_back(); // remove trailing null
141 auto findIndex = baseConfiguration->second.find("Index");
142 if (findIndex == baseConfiguration->second.end())
143 {
144 std::cerr << baseConfiguration->first << " missing index\n";
145 continue;
146 }
147 unsigned int configIndex = mapbox::util::apply_visitor(
148 VariantToUnsignedIntVisitor(), findIndex->second);
149
150 if (configIndex != index)
151 {
152 continue;
153 }
154 // now that the indexes match, verify the connector
155 auto findConnectorName = connector->second.find("Name");
156 if (findConnectorName == connector->second.end())
157 {
158 continue;
159 }
160 std::string connectorName = mapbox::util::apply_visitor(
161 VariantToStringVisitor(), findConnectorName->second);
162 boost::replace_all(connectorName, " ", "_");
163 if (connectorName == oemName)
164 {
165 sensorData = &(sensor.second);
166 break;
167 }
168 }
169 if (sensorData == nullptr)
170 {
171 std::cerr << "failed to find match for " << path.string() << "\n";
172 continue;
173 }
174
175 auto findSensorName = baseConfiguration->second.find("Name");
176 if (findSensorName == baseConfiguration->second.end())
177 {
178 std::cerr << "could not determine configuration name for "
179 << path.string() << "\n";
180 continue;
181 }
182 std::string sensorName =
183 sdbusplus::message::variant_ns::get<std::string>(
184 findSensorName->second);
185 // on rescans, only update sensors we were signaled by
186 auto findSensor = tachSensors.find(sensorName);
187 if (!firstScan && findSensor != tachSensors.end())
188 {
189 bool found = false;
190 for (auto it = sensorsChanged->begin(); it != sensorsChanged->end();
191 it++)
192 {
193 if (boost::ends_with(*it, findSensor->second->name))
194 {
195 sensorsChanged->erase(it);
196 findSensor->second = nullptr;
197 found = true;
198 break;
199 }
200 }
201 if (!found)
202 {
203 continue;
204 }
205 }
206 std::vector<thresholds::Threshold> sensorThresholds;
207 if (!ParseThresholdsFromConfig(*sensorData, sensorThresholds))
208 {
209 std::cerr << "error populating thresholds for " << sensorName
210 << "\n";
211 }
212
213 tachSensors[sensorName] = std::make_unique<TachSensor>(
214 path.string(), objectServer, dbusConnection, io, sensorName,
215 std::move(sensorThresholds), *interfacePath);
216 }
217 std::vector<fs::path> pwms;
218 if (!find_files(fs::path("/sys/class/hwmon"), R"(pwm\d+)", pwms))
219 {
220 std::cerr << "No pwm in system\n";
221 return;
222 }
223 for (const fs::path& pwm : pwms)
224 {
225 // only add new elements
226 pwmSensors.insert(std::pair<std::string, std::unique_ptr<PwmSensor>>(
227 pwm.string(),
228 std::make_unique<PwmSensor>(pwm.string(), objectServer)));
229 }
230}
231
232int main(int argc, char** argv)
233{
234 boost::asio::io_service io;
235 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
236 systemBus->request_name("xyz.openbmc_project.FanSensor");
237 sdbusplus::asio::object_server objectServer(systemBus);
238 boost::container::flat_map<std::string, std::unique_ptr<TachSensor>>
239 tachSensors;
240 boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
241 pwmSensors;
242 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
243 std::unique_ptr<boost::container::flat_set<std::string>> sensorsChanged =
244 std::make_unique<boost::container::flat_set<std::string>>();
245
246 io.post([&]() {
247 createSensors(io, objectServer, tachSensors, pwmSensors, systemBus,
248 nullptr);
249 });
250
251 boost::asio::deadline_timer filterTimer(io);
252 std::function<void(sdbusplus::message::message&)> eventHandler =
253 [&](sdbusplus::message::message& message) {
254 if (message.is_method_error())
255 {
256 std::cerr << "callback method error\n";
257 return;
258 }
259 sensorsChanged->insert(message.get_path());
260 // this implicitly cancels the timer
261 filterTimer.expires_from_now(boost::posix_time::seconds(1));
262
263 filterTimer.async_wait([&](const boost::system::error_code& ec) {
264 if (ec == boost::asio::error::operation_aborted)
265 {
266 /* we were canceled*/
267 return;
268 }
269 else if (ec)
270 {
271 std::cerr << "timer error\n";
272 return;
273 }
274 createSensors(io, objectServer, tachSensors, pwmSensors,
275 systemBus, sensorsChanged);
276 });
277 };
278
279 for (const char* type : SENSOR_TYPES)
280 {
281 auto match = std::make_unique<sdbusplus::bus::match::match>(
282 static_cast<sdbusplus::bus::bus&>(*systemBus),
283 "type='signal',member='PropertiesChanged',path_namespace='" +
284 std::string(INVENTORY_PATH) + "',arg0namespace='" + type + "'",
285 eventHandler);
286 matches.emplace_back(std::move(match));
287 }
288
289 io.run();
290}