blob: 542ca78258152333717a4bf599529da03372bdb5 [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"};
Jae Hyun Yoof78ec412018-10-25 10:42:39 -070037static std::regex inputRegex(R"(fan(\d+)_input)");
James Feist139cb572018-09-10 15:26:18 -070038
39void createSensors(
40 boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
41 boost::container::flat_map<std::string, std::unique_ptr<TachSensor>>&
42 tachSensors,
43 boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>&
44 pwmSensors,
45 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
46 const std::unique_ptr<boost::container::flat_set<std::string>>&
47 sensorsChanged)
48{
49 bool firstScan = sensorsChanged == nullptr;
50 // use new data the first time, then refresh
51 ManagedObjectType sensorConfigurations;
52 bool useCache = false;
Jae Hyun Yoof78ec412018-10-25 10:42:39 -070053 for (const char* type : sensorTypes)
James Feist139cb572018-09-10 15:26:18 -070054 {
55 if (!getSensorConfiguration(type, dbusConnection, sensorConfigurations,
56 useCache))
57 {
58 std::cerr << "error communicating to entity manager\n";
59 return;
60 }
61 useCache = true;
62 }
63 std::vector<fs::path> paths;
Jae Hyun Yoof78ec412018-10-25 10:42:39 -070064 if (!findFiles(fs::path("/sys/class/hwmon"), R"(fan\d+_input)", paths))
James Feist139cb572018-09-10 15:26:18 -070065 {
66 std::cerr << "No temperature sensors in system\n";
67 return;
68 }
69
70 // iterate through all found fan sensors, and try to match them with
71 // configuration
72 for (auto& path : paths)
73 {
74 std::smatch match;
75 std::string pathStr = path.string();
76
Jae Hyun Yoof78ec412018-10-25 10:42:39 -070077 std::regex_search(pathStr, match, inputRegex);
James Feist139cb572018-09-10 15:26:18 -070078 std::string indexStr = *(match.begin() + 1);
79
80 auto directory = path.parent_path();
81 // convert to 0 based
82 size_t index = std::stoul(indexStr) - 1;
83
84 const char* baseType;
85 const SensorData* sensorData = nullptr;
86 const std::string* interfacePath = nullptr;
87 const std::pair<std::string, boost::container::flat_map<
88 std::string, BasicVariantType>>*
89 baseConfiguration = nullptr;
90 for (const std::pair<sdbusplus::message::object_path, SensorData>&
91 sensor : sensorConfigurations)
92 {
93 // find the base of the configuration to see if indexes match
Jae Hyun Yoof78ec412018-10-25 10:42:39 -070094 for (const char* type : sensorTypes)
James Feist139cb572018-09-10 15:26:18 -070095 {
96 auto sensorBaseFind = sensor.second.find(type);
97 if (sensorBaseFind != sensor.second.end())
98 {
99 baseConfiguration = &(*sensorBaseFind);
100 interfacePath = &(sensor.first.str);
101 baseType = type;
102 break;
103 }
104 }
105 if (baseConfiguration == nullptr)
106 {
107 continue;
108 }
109 auto connector =
110 sensor.second.find(baseType + std::string(".Connector"));
111 if (connector == sensor.second.end())
112 {
113 std::cerr << baseConfiguration->first << " missing connector\n";
114 continue;
115 }
116 auto findPwmIndex = connector->second.find("Pwm");
117 if (findPwmIndex == connector->second.end())
118 {
119 continue;
120 }
Yoo, Jae Hyun625429b2018-10-17 18:19:02 -0700121 uint16_t pwmIndex = variant_ns::visit(VariantToUnsignedIntVisitor(),
122 findPwmIndex->second);
James Feist139cb572018-09-10 15:26:18 -0700123 auto oemNamePath = directory.string() + R"(/of_node/oemname)" +
124 std::to_string(pwmIndex);
125
126 if (DEBUG)
Jae Hyun Yoof78ec412018-10-25 10:42:39 -0700127 {
James Feist139cb572018-09-10 15:26:18 -0700128 std::cout << "Checking path " << oemNamePath << "\n";
Jae Hyun Yoof78ec412018-10-25 10:42:39 -0700129 }
James Feist139cb572018-09-10 15:26:18 -0700130 std::ifstream nameFile(oemNamePath);
131 if (!nameFile.good())
132 {
133 continue;
134 }
135 std::string oemName;
136 std::getline(nameFile, oemName);
137 nameFile.close();
138 if (!oemName.size())
139 {
140 // shouldn't have an empty name file
141 continue;
142 }
143 oemName.pop_back(); // remove trailing null
144 auto findIndex = baseConfiguration->second.find("Index");
145 if (findIndex == baseConfiguration->second.end())
146 {
147 std::cerr << baseConfiguration->first << " missing index\n";
148 continue;
149 }
Yoo, Jae Hyun625429b2018-10-17 18:19:02 -0700150 unsigned int configIndex = variant_ns::visit(
James Feist139cb572018-09-10 15:26:18 -0700151 VariantToUnsignedIntVisitor(), findIndex->second);
152
153 if (configIndex != index)
154 {
155 continue;
156 }
157 // now that the indexes match, verify the connector
158 auto findConnectorName = connector->second.find("Name");
159 if (findConnectorName == connector->second.end())
160 {
161 continue;
162 }
Yoo, Jae Hyun625429b2018-10-17 18:19:02 -0700163 std::string connectorName = variant_ns::visit(
James Feist139cb572018-09-10 15:26:18 -0700164 VariantToStringVisitor(), findConnectorName->second);
165 boost::replace_all(connectorName, " ", "_");
166 if (connectorName == oemName)
167 {
168 sensorData = &(sensor.second);
169 break;
170 }
171 }
172 if (sensorData == nullptr)
173 {
174 std::cerr << "failed to find match for " << path.string() << "\n";
175 continue;
176 }
177
178 auto findSensorName = baseConfiguration->second.find("Name");
179 if (findSensorName == baseConfiguration->second.end())
180 {
181 std::cerr << "could not determine configuration name for "
182 << path.string() << "\n";
183 continue;
184 }
185 std::string sensorName =
186 sdbusplus::message::variant_ns::get<std::string>(
187 findSensorName->second);
188 // on rescans, only update sensors we were signaled by
189 auto findSensor = tachSensors.find(sensorName);
190 if (!firstScan && findSensor != tachSensors.end())
191 {
192 bool found = false;
193 for (auto it = sensorsChanged->begin(); it != sensorsChanged->end();
194 it++)
195 {
196 if (boost::ends_with(*it, findSensor->second->name))
197 {
198 sensorsChanged->erase(it);
199 findSensor->second = nullptr;
200 found = true;
201 break;
202 }
203 }
204 if (!found)
205 {
206 continue;
207 }
208 }
209 std::vector<thresholds::Threshold> sensorThresholds;
Jae Hyun Yoof78ec412018-10-25 10:42:39 -0700210 if (!parseThresholdsFromConfig(*sensorData, sensorThresholds))
James Feist139cb572018-09-10 15:26:18 -0700211 {
212 std::cerr << "error populating thresholds for " << sensorName
213 << "\n";
214 }
215
James Feist7c391352018-10-26 14:09:45 -0700216 auto presenceConfig =
217 sensorData->find(baseType + std::string(".Presence"));
218
219 std::unique_ptr<PresenceSensor> presenceSensor(nullptr);
220
221 // presence sensors are optional
222 if (presenceConfig != sensorData->end())
223 {
224 auto findIndex = presenceConfig->second.find("Index");
225 auto findPolarity = presenceConfig->second.find("Polarity");
226
227 if (findIndex == presenceConfig->second.end() ||
228 findPolarity == presenceConfig->second.end())
229 {
230 std::cerr << "Malformed Presence Configuration\n";
231 }
232 else
233 {
234 size_t index = variant_ns::get<uint64_t>(findIndex->second);
235 bool inverted =
236 variant_ns::get<std::string>(findPolarity->second) == "Low";
237 presenceSensor =
238 std::make_unique<PresenceSensor>(index, inverted, io);
239 }
240 }
241
James Feist139cb572018-09-10 15:26:18 -0700242 tachSensors[sensorName] = std::make_unique<TachSensor>(
James Feist7c391352018-10-26 14:09:45 -0700243 path.string(), objectServer, dbusConnection,
244 std::move(presenceSensor), io, sensorName,
James Feist139cb572018-09-10 15:26:18 -0700245 std::move(sensorThresholds), *interfacePath);
246 }
247 std::vector<fs::path> pwms;
Jae Hyun Yoof78ec412018-10-25 10:42:39 -0700248 if (!findFiles(fs::path("/sys/class/hwmon"), R"(pwm\d+)", pwms))
James Feist139cb572018-09-10 15:26:18 -0700249 {
250 std::cerr << "No pwm in system\n";
251 return;
252 }
253 for (const fs::path& pwm : pwms)
254 {
255 // only add new elements
256 pwmSensors.insert(std::pair<std::string, std::unique_ptr<PwmSensor>>(
257 pwm.string(),
258 std::make_unique<PwmSensor>(pwm.string(), objectServer)));
259 }
260}
261
262int main(int argc, char** argv)
263{
264 boost::asio::io_service io;
265 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
266 systemBus->request_name("xyz.openbmc_project.FanSensor");
267 sdbusplus::asio::object_server objectServer(systemBus);
268 boost::container::flat_map<std::string, std::unique_ptr<TachSensor>>
269 tachSensors;
270 boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
271 pwmSensors;
272 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
273 std::unique_ptr<boost::container::flat_set<std::string>> sensorsChanged =
274 std::make_unique<boost::container::flat_set<std::string>>();
275
276 io.post([&]() {
277 createSensors(io, objectServer, tachSensors, pwmSensors, systemBus,
278 nullptr);
279 });
280
281 boost::asio::deadline_timer filterTimer(io);
282 std::function<void(sdbusplus::message::message&)> eventHandler =
283 [&](sdbusplus::message::message& message) {
284 if (message.is_method_error())
285 {
286 std::cerr << "callback method error\n";
287 return;
288 }
289 sensorsChanged->insert(message.get_path());
290 // this implicitly cancels the timer
291 filterTimer.expires_from_now(boost::posix_time::seconds(1));
292
293 filterTimer.async_wait([&](const boost::system::error_code& ec) {
294 if (ec == boost::asio::error::operation_aborted)
295 {
296 /* we were canceled*/
297 return;
298 }
299 else if (ec)
300 {
301 std::cerr << "timer error\n";
302 return;
303 }
304 createSensors(io, objectServer, tachSensors, pwmSensors,
305 systemBus, sensorsChanged);
306 });
307 };
308
Jae Hyun Yoof78ec412018-10-25 10:42:39 -0700309 for (const char* type : sensorTypes)
James Feist139cb572018-09-10 15:26:18 -0700310 {
311 auto match = std::make_unique<sdbusplus::bus::match::match>(
312 static_cast<sdbusplus::bus::bus&>(*systemBus),
313 "type='signal',member='PropertiesChanged',path_namespace='" +
Jae Hyun Yoof78ec412018-10-25 10:42:39 -0700314 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
James Feist139cb572018-09-10 15:26:18 -0700315 eventHandler);
316 matches.emplace_back(std::move(match));
317 }
318
319 io.run();
320}