blob: b562e05849a10bb6009a32f32799e850d7370c38 [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;
James Feist139cb572018-09-10 15:26:18 -070035static constexpr std::array<const char*, 1> SENSOR_TYPES = {
36 "xyz.openbmc_project.Configuration.AspeedFan"};
37static std::regex INPUT_REGEX(R"(fan(\d+)_input)");
38
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;
53 for (const char* type : SENSOR_TYPES)
54 {
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;
64 if (!find_files(fs::path("/sys/class/hwmon"), R"(fan\d+_input)", paths))
65 {
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
77 std::regex_search(pathStr, match, INPUT_REGEX);
78 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
94 for (const char* type : SENSOR_TYPES)
95 {
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)
127 std::cout << "Checking path " << oemNamePath << "\n";
128 std::ifstream nameFile(oemNamePath);
129 if (!nameFile.good())
130 {
131 continue;
132 }
133 std::string oemName;
134 std::getline(nameFile, oemName);
135 nameFile.close();
136 if (!oemName.size())
137 {
138 // shouldn't have an empty name file
139 continue;
140 }
141 oemName.pop_back(); // remove trailing null
142 auto findIndex = baseConfiguration->second.find("Index");
143 if (findIndex == baseConfiguration->second.end())
144 {
145 std::cerr << baseConfiguration->first << " missing index\n";
146 continue;
147 }
Yoo, Jae Hyun625429b2018-10-17 18:19:02 -0700148 unsigned int configIndex = variant_ns::visit(
James Feist139cb572018-09-10 15:26:18 -0700149 VariantToUnsignedIntVisitor(), findIndex->second);
150
151 if (configIndex != index)
152 {
153 continue;
154 }
155 // now that the indexes match, verify the connector
156 auto findConnectorName = connector->second.find("Name");
157 if (findConnectorName == connector->second.end())
158 {
159 continue;
160 }
Yoo, Jae Hyun625429b2018-10-17 18:19:02 -0700161 std::string connectorName = variant_ns::visit(
James Feist139cb572018-09-10 15:26:18 -0700162 VariantToStringVisitor(), findConnectorName->second);
163 boost::replace_all(connectorName, " ", "_");
164 if (connectorName == oemName)
165 {
166 sensorData = &(sensor.second);
167 break;
168 }
169 }
170 if (sensorData == nullptr)
171 {
172 std::cerr << "failed to find match for " << path.string() << "\n";
173 continue;
174 }
175
176 auto findSensorName = baseConfiguration->second.find("Name");
177 if (findSensorName == baseConfiguration->second.end())
178 {
179 std::cerr << "could not determine configuration name for "
180 << path.string() << "\n";
181 continue;
182 }
183 std::string sensorName =
184 sdbusplus::message::variant_ns::get<std::string>(
185 findSensorName->second);
186 // on rescans, only update sensors we were signaled by
187 auto findSensor = tachSensors.find(sensorName);
188 if (!firstScan && findSensor != tachSensors.end())
189 {
190 bool found = false;
191 for (auto it = sensorsChanged->begin(); it != sensorsChanged->end();
192 it++)
193 {
194 if (boost::ends_with(*it, findSensor->second->name))
195 {
196 sensorsChanged->erase(it);
197 findSensor->second = nullptr;
198 found = true;
199 break;
200 }
201 }
202 if (!found)
203 {
204 continue;
205 }
206 }
207 std::vector<thresholds::Threshold> sensorThresholds;
208 if (!ParseThresholdsFromConfig(*sensorData, sensorThresholds))
209 {
210 std::cerr << "error populating thresholds for " << sensorName
211 << "\n";
212 }
213
214 tachSensors[sensorName] = std::make_unique<TachSensor>(
215 path.string(), objectServer, dbusConnection, io, sensorName,
216 std::move(sensorThresholds), *interfacePath);
217 }
218 std::vector<fs::path> pwms;
219 if (!find_files(fs::path("/sys/class/hwmon"), R"(pwm\d+)", pwms))
220 {
221 std::cerr << "No pwm in system\n";
222 return;
223 }
224 for (const fs::path& pwm : pwms)
225 {
226 // only add new elements
227 pwmSensors.insert(std::pair<std::string, std::unique_ptr<PwmSensor>>(
228 pwm.string(),
229 std::make_unique<PwmSensor>(pwm.string(), objectServer)));
230 }
231}
232
233int main(int argc, char** argv)
234{
235 boost::asio::io_service io;
236 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
237 systemBus->request_name("xyz.openbmc_project.FanSensor");
238 sdbusplus::asio::object_server objectServer(systemBus);
239 boost::container::flat_map<std::string, std::unique_ptr<TachSensor>>
240 tachSensors;
241 boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
242 pwmSensors;
243 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
244 std::unique_ptr<boost::container::flat_set<std::string>> sensorsChanged =
245 std::make_unique<boost::container::flat_set<std::string>>();
246
247 io.post([&]() {
248 createSensors(io, objectServer, tachSensors, pwmSensors, systemBus,
249 nullptr);
250 });
251
252 boost::asio::deadline_timer filterTimer(io);
253 std::function<void(sdbusplus::message::message&)> eventHandler =
254 [&](sdbusplus::message::message& message) {
255 if (message.is_method_error())
256 {
257 std::cerr << "callback method error\n";
258 return;
259 }
260 sensorsChanged->insert(message.get_path());
261 // this implicitly cancels the timer
262 filterTimer.expires_from_now(boost::posix_time::seconds(1));
263
264 filterTimer.async_wait([&](const boost::system::error_code& ec) {
265 if (ec == boost::asio::error::operation_aborted)
266 {
267 /* we were canceled*/
268 return;
269 }
270 else if (ec)
271 {
272 std::cerr << "timer error\n";
273 return;
274 }
275 createSensors(io, objectServer, tachSensors, pwmSensors,
276 systemBus, sensorsChanged);
277 });
278 };
279
280 for (const char* type : SENSOR_TYPES)
281 {
282 auto match = std::make_unique<sdbusplus::bus::match::match>(
283 static_cast<sdbusplus::bus::bus&>(*systemBus),
284 "type='signal',member='PropertiesChanged',path_namespace='" +
285 std::string(INVENTORY_PATH) + "',arg0namespace='" + type + "'",
286 eventHandler);
287 matches.emplace_back(std::move(match));
288 }
289
290 io.run();
291}