blob: 84013437e3da5e989f26d1590586c5af0a325c7a [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;
Yoo, Jae Hyun625429b2018-10-17 18:19:02 -070034namespace variant_ns = sdbusplus::message::variant_ns;
Jae Hyun Yoof78ec412018-10-25 10:42:39 -070035static constexpr std::array<const char*, 1> sensorTypes = {
James Feist139cb572018-09-10 15:26:18 -070036 "xyz.openbmc_project.Configuration.AspeedFan"};
James Feist39132a62018-10-31 12:53:20 -070037constexpr const char* redundancyConfiguration =
38 "xyz.openbmc_project.Configuration.FanRedundancy";
Jae Hyun Yoof78ec412018-10-25 10:42:39 -070039static std::regex inputRegex(R"(fan(\d+)_input)");
James Feist139cb572018-09-10 15:26:18 -070040
James Feist39132a62018-10-31 12:53:20 -070041// todo: power supply fan redundancy
42std::unique_ptr<RedundancySensor> systemRedundancy = nullptr;
43
James Feist139cb572018-09-10 15:26:18 -070044void createSensors(
45 boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
46 boost::container::flat_map<std::string, std::unique_ptr<TachSensor>>&
47 tachSensors,
48 boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>&
49 pwmSensors,
50 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
51 const std::unique_ptr<boost::container::flat_set<std::string>>&
52 sensorsChanged)
53{
54 bool firstScan = sensorsChanged == nullptr;
55 // use new data the first time, then refresh
56 ManagedObjectType sensorConfigurations;
57 bool useCache = false;
Jae Hyun Yoof78ec412018-10-25 10:42:39 -070058 for (const char* type : sensorTypes)
James Feist139cb572018-09-10 15:26:18 -070059 {
60 if (!getSensorConfiguration(type, dbusConnection, sensorConfigurations,
61 useCache))
62 {
63 std::cerr << "error communicating to entity manager\n";
64 return;
65 }
66 useCache = true;
67 }
68 std::vector<fs::path> paths;
Jae Hyun Yoof78ec412018-10-25 10:42:39 -070069 if (!findFiles(fs::path("/sys/class/hwmon"), R"(fan\d+_input)", paths))
James Feist139cb572018-09-10 15:26:18 -070070 {
71 std::cerr << "No temperature sensors in system\n";
72 return;
73 }
74
75 // iterate through all found fan sensors, and try to match them with
76 // configuration
77 for (auto& path : paths)
78 {
79 std::smatch match;
80 std::string pathStr = path.string();
81
Jae Hyun Yoof78ec412018-10-25 10:42:39 -070082 std::regex_search(pathStr, match, inputRegex);
James Feist139cb572018-09-10 15:26:18 -070083 std::string indexStr = *(match.begin() + 1);
84
85 auto directory = path.parent_path();
86 // convert to 0 based
87 size_t index = std::stoul(indexStr) - 1;
88
89 const char* baseType;
90 const SensorData* sensorData = nullptr;
91 const std::string* interfacePath = nullptr;
92 const std::pair<std::string, boost::container::flat_map<
93 std::string, BasicVariantType>>*
94 baseConfiguration = nullptr;
95 for (const std::pair<sdbusplus::message::object_path, SensorData>&
96 sensor : sensorConfigurations)
97 {
98 // find the base of the configuration to see if indexes match
Jae Hyun Yoof78ec412018-10-25 10:42:39 -070099 for (const char* type : sensorTypes)
James Feist139cb572018-09-10 15:26:18 -0700100 {
101 auto sensorBaseFind = sensor.second.find(type);
102 if (sensorBaseFind != sensor.second.end())
103 {
104 baseConfiguration = &(*sensorBaseFind);
105 interfacePath = &(sensor.first.str);
106 baseType = type;
107 break;
108 }
109 }
110 if (baseConfiguration == nullptr)
111 {
112 continue;
113 }
114 auto connector =
115 sensor.second.find(baseType + std::string(".Connector"));
116 if (connector == sensor.second.end())
117 {
118 std::cerr << baseConfiguration->first << " missing connector\n";
119 continue;
120 }
121 auto findPwmIndex = connector->second.find("Pwm");
122 if (findPwmIndex == connector->second.end())
123 {
124 continue;
125 }
Yoo, Jae Hyun625429b2018-10-17 18:19:02 -0700126 uint16_t pwmIndex = variant_ns::visit(VariantToUnsignedIntVisitor(),
127 findPwmIndex->second);
James Feist139cb572018-09-10 15:26:18 -0700128 auto oemNamePath = directory.string() + R"(/of_node/oemname)" +
129 std::to_string(pwmIndex);
130
131 if (DEBUG)
Jae Hyun Yoof78ec412018-10-25 10:42:39 -0700132 {
James Feist139cb572018-09-10 15:26:18 -0700133 std::cout << "Checking path " << oemNamePath << "\n";
Jae Hyun Yoof78ec412018-10-25 10:42:39 -0700134 }
James Feist139cb572018-09-10 15:26:18 -0700135 std::ifstream nameFile(oemNamePath);
136 if (!nameFile.good())
137 {
138 continue;
139 }
140 std::string oemName;
141 std::getline(nameFile, oemName);
142 nameFile.close();
143 if (!oemName.size())
144 {
145 // shouldn't have an empty name file
146 continue;
147 }
148 oemName.pop_back(); // remove trailing null
149 auto findIndex = baseConfiguration->second.find("Index");
150 if (findIndex == baseConfiguration->second.end())
151 {
152 std::cerr << baseConfiguration->first << " missing index\n";
153 continue;
154 }
Yoo, Jae Hyun625429b2018-10-17 18:19:02 -0700155 unsigned int configIndex = variant_ns::visit(
James Feist139cb572018-09-10 15:26:18 -0700156 VariantToUnsignedIntVisitor(), findIndex->second);
157
158 if (configIndex != index)
159 {
160 continue;
161 }
162 // now that the indexes match, verify the connector
163 auto findConnectorName = connector->second.find("Name");
164 if (findConnectorName == connector->second.end())
165 {
166 continue;
167 }
Yoo, Jae Hyun625429b2018-10-17 18:19:02 -0700168 std::string connectorName = variant_ns::visit(
James Feist139cb572018-09-10 15:26:18 -0700169 VariantToStringVisitor(), findConnectorName->second);
170 boost::replace_all(connectorName, " ", "_");
171 if (connectorName == oemName)
172 {
173 sensorData = &(sensor.second);
174 break;
175 }
176 }
177 if (sensorData == nullptr)
178 {
179 std::cerr << "failed to find match for " << path.string() << "\n";
180 continue;
181 }
182
183 auto findSensorName = baseConfiguration->second.find("Name");
184 if (findSensorName == baseConfiguration->second.end())
185 {
186 std::cerr << "could not determine configuration name for "
187 << path.string() << "\n";
188 continue;
189 }
190 std::string sensorName =
191 sdbusplus::message::variant_ns::get<std::string>(
192 findSensorName->second);
193 // on rescans, only update sensors we were signaled by
194 auto findSensor = tachSensors.find(sensorName);
195 if (!firstScan && findSensor != tachSensors.end())
196 {
197 bool found = false;
198 for (auto it = sensorsChanged->begin(); it != sensorsChanged->end();
199 it++)
200 {
201 if (boost::ends_with(*it, findSensor->second->name))
202 {
203 sensorsChanged->erase(it);
204 findSensor->second = nullptr;
205 found = true;
206 break;
207 }
208 }
209 if (!found)
210 {
211 continue;
212 }
213 }
214 std::vector<thresholds::Threshold> sensorThresholds;
Jae Hyun Yoof78ec412018-10-25 10:42:39 -0700215 if (!parseThresholdsFromConfig(*sensorData, sensorThresholds))
James Feist139cb572018-09-10 15:26:18 -0700216 {
217 std::cerr << "error populating thresholds for " << sensorName
218 << "\n";
219 }
220
James Feist7c391352018-10-26 14:09:45 -0700221 auto presenceConfig =
222 sensorData->find(baseType + std::string(".Presence"));
223
224 std::unique_ptr<PresenceSensor> presenceSensor(nullptr);
225
226 // presence sensors are optional
227 if (presenceConfig != sensorData->end())
228 {
229 auto findIndex = presenceConfig->second.find("Index");
230 auto findPolarity = presenceConfig->second.find("Polarity");
231
232 if (findIndex == presenceConfig->second.end() ||
233 findPolarity == presenceConfig->second.end())
234 {
235 std::cerr << "Malformed Presence Configuration\n";
236 }
237 else
238 {
239 size_t index = variant_ns::get<uint64_t>(findIndex->second);
240 bool inverted =
241 variant_ns::get<std::string>(findPolarity->second) == "Low";
242 presenceSensor =
243 std::make_unique<PresenceSensor>(index, inverted, io);
244 }
245 }
246
James Feist139cb572018-09-10 15:26:18 -0700247 tachSensors[sensorName] = std::make_unique<TachSensor>(
James Feist7c391352018-10-26 14:09:45 -0700248 path.string(), objectServer, dbusConnection,
James Feist39132a62018-10-31 12:53:20 -0700249 std::move(presenceSensor), systemRedundancy, io, sensorName,
James Feist139cb572018-09-10 15:26:18 -0700250 std::move(sensorThresholds), *interfacePath);
251 }
252 std::vector<fs::path> pwms;
Jae Hyun Yoof78ec412018-10-25 10:42:39 -0700253 if (!findFiles(fs::path("/sys/class/hwmon"), R"(pwm\d+)", pwms))
James Feist139cb572018-09-10 15:26:18 -0700254 {
255 std::cerr << "No pwm in system\n";
256 return;
257 }
258 for (const fs::path& pwm : pwms)
259 {
260 // only add new elements
261 pwmSensors.insert(std::pair<std::string, std::unique_ptr<PwmSensor>>(
262 pwm.string(),
263 std::make_unique<PwmSensor>(pwm.string(), objectServer)));
264 }
265}
266
James Feist39132a62018-10-31 12:53:20 -0700267void createRedundancySensor(
268 const boost::container::flat_map<std::string, std::unique_ptr<TachSensor>>&
269 sensors,
270 std::shared_ptr<sdbusplus::asio::connection> conn,
271 sdbusplus::asio::object_server& objectServer)
272{
273
274 conn->async_method_call(
275 [&objectServer, &sensors](boost::system::error_code& ec,
276 const ManagedObjectType managedObj) {
277 if (ec)
278 {
279 std::cerr << "Error calling entity manager \n";
280 return;
281 }
282 for (const auto& pathPair : managedObj)
283 {
284 for (const auto& interfacePair : pathPair.second)
285 {
286 if (interfacePair.first == redundancyConfiguration)
287 {
288 // currently only support one
289 auto findCount =
290 interfacePair.second.find("AllowedFailures");
291 if (findCount == interfacePair.second.end())
292 {
293 std::cerr << "Malformed redundancy record \n";
294 return;
295 }
296 std::vector<std::string> sensorList;
297
298 for (const auto& sensor : sensors)
299 {
300 sensorList.push_back(
301 "/xyz/openbmc_project/sensors/fan_tach/" +
302 sensor.second->name);
303 }
304 systemRedundancy = std::make_unique<RedundancySensor>(
305 variant_ns::get<uint64_t>(findCount->second),
306 sensorList, objectServer);
307
308 return;
309 }
310 }
311 }
312 },
313 "xyz.openbmc_project.EntityManager", "/",
314 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
315}
316
James Feist139cb572018-09-10 15:26:18 -0700317int main(int argc, char** argv)
318{
319 boost::asio::io_service io;
320 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
321 systemBus->request_name("xyz.openbmc_project.FanSensor");
322 sdbusplus::asio::object_server objectServer(systemBus);
323 boost::container::flat_map<std::string, std::unique_ptr<TachSensor>>
324 tachSensors;
325 boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
326 pwmSensors;
327 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
328 std::unique_ptr<boost::container::flat_set<std::string>> sensorsChanged =
329 std::make_unique<boost::container::flat_set<std::string>>();
330
331 io.post([&]() {
332 createSensors(io, objectServer, tachSensors, pwmSensors, systemBus,
333 nullptr);
James Feist39132a62018-10-31 12:53:20 -0700334 createRedundancySensor(tachSensors, systemBus, objectServer);
James Feist139cb572018-09-10 15:26:18 -0700335 });
336
337 boost::asio::deadline_timer filterTimer(io);
338 std::function<void(sdbusplus::message::message&)> eventHandler =
339 [&](sdbusplus::message::message& message) {
340 if (message.is_method_error())
341 {
342 std::cerr << "callback method error\n";
343 return;
344 }
345 sensorsChanged->insert(message.get_path());
346 // this implicitly cancels the timer
347 filterTimer.expires_from_now(boost::posix_time::seconds(1));
348
349 filterTimer.async_wait([&](const boost::system::error_code& ec) {
350 if (ec == boost::asio::error::operation_aborted)
351 {
352 /* we were canceled*/
353 return;
354 }
355 else if (ec)
356 {
357 std::cerr << "timer error\n";
358 return;
359 }
360 createSensors(io, objectServer, tachSensors, pwmSensors,
361 systemBus, sensorsChanged);
362 });
363 };
364
Jae Hyun Yoof78ec412018-10-25 10:42:39 -0700365 for (const char* type : sensorTypes)
James Feist139cb572018-09-10 15:26:18 -0700366 {
367 auto match = std::make_unique<sdbusplus::bus::match::match>(
368 static_cast<sdbusplus::bus::bus&>(*systemBus),
369 "type='signal',member='PropertiesChanged',path_namespace='" +
Jae Hyun Yoof78ec412018-10-25 10:42:39 -0700370 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
James Feist139cb572018-09-10 15:26:18 -0700371 eventHandler);
372 matches.emplace_back(std::move(match));
373 }
374
James Feist39132a62018-10-31 12:53:20 -0700375 // redundancy sensor
376 std::function<void(sdbusplus::message::message&)> redundancyHandler =
377 [&tachSensors, &systemBus,
378 &objectServer](sdbusplus::message::message& message) {
379 createRedundancySensor(tachSensors, systemBus, objectServer);
380 };
381 auto match = std::make_unique<sdbusplus::bus::match::match>(
382 static_cast<sdbusplus::bus::bus&>(*systemBus),
383 "type='signal',member='PropertiesChanged',path_namespace='" +
384 std::string(inventoryPath) + "',arg0namespace='" +
385 redundancyConfiguration + "'",
386 redundancyHandler);
387 matches.emplace_back(std::move(match));
388
James Feist139cb572018-09-10 15:26:18 -0700389 io.run();
390}