blob: 4fa9ff238acb380edc57c4ccb5e80c4edbed8d59 [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
216 tachSensors[sensorName] = std::make_unique<TachSensor>(
217 path.string(), objectServer, dbusConnection, io, sensorName,
218 std::move(sensorThresholds), *interfacePath);
219 }
220 std::vector<fs::path> pwms;
Jae Hyun Yoof78ec412018-10-25 10:42:39 -0700221 if (!findFiles(fs::path("/sys/class/hwmon"), R"(pwm\d+)", pwms))
James Feist139cb572018-09-10 15:26:18 -0700222 {
223 std::cerr << "No pwm in system\n";
224 return;
225 }
226 for (const fs::path& pwm : pwms)
227 {
228 // only add new elements
229 pwmSensors.insert(std::pair<std::string, std::unique_ptr<PwmSensor>>(
230 pwm.string(),
231 std::make_unique<PwmSensor>(pwm.string(), objectServer)));
232 }
233}
234
235int main(int argc, char** argv)
236{
237 boost::asio::io_service io;
238 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
239 systemBus->request_name("xyz.openbmc_project.FanSensor");
240 sdbusplus::asio::object_server objectServer(systemBus);
241 boost::container::flat_map<std::string, std::unique_ptr<TachSensor>>
242 tachSensors;
243 boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
244 pwmSensors;
245 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
246 std::unique_ptr<boost::container::flat_set<std::string>> sensorsChanged =
247 std::make_unique<boost::container::flat_set<std::string>>();
248
249 io.post([&]() {
250 createSensors(io, objectServer, tachSensors, pwmSensors, systemBus,
251 nullptr);
252 });
253
254 boost::asio::deadline_timer filterTimer(io);
255 std::function<void(sdbusplus::message::message&)> eventHandler =
256 [&](sdbusplus::message::message& message) {
257 if (message.is_method_error())
258 {
259 std::cerr << "callback method error\n";
260 return;
261 }
262 sensorsChanged->insert(message.get_path());
263 // this implicitly cancels the timer
264 filterTimer.expires_from_now(boost::posix_time::seconds(1));
265
266 filterTimer.async_wait([&](const boost::system::error_code& ec) {
267 if (ec == boost::asio::error::operation_aborted)
268 {
269 /* we were canceled*/
270 return;
271 }
272 else if (ec)
273 {
274 std::cerr << "timer error\n";
275 return;
276 }
277 createSensors(io, objectServer, tachSensors, pwmSensors,
278 systemBus, sensorsChanged);
279 });
280 };
281
Jae Hyun Yoof78ec412018-10-25 10:42:39 -0700282 for (const char* type : sensorTypes)
James Feist139cb572018-09-10 15:26:18 -0700283 {
284 auto match = std::make_unique<sdbusplus::bus::match::match>(
285 static_cast<sdbusplus::bus::bus&>(*systemBus),
286 "type='signal',member='PropertiesChanged',path_namespace='" +
Jae Hyun Yoof78ec412018-10-25 10:42:39 -0700287 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
James Feist139cb572018-09-10 15:26:18 -0700288 eventHandler);
289 matches.emplace_back(std::move(match));
290 }
291
292 io.run();
293}