blob: bcbb5a56e9632fd119e9821fe675e9c3a8720445 [file] [log] [blame]
Cheng C Yang209ec562019-03-12 16:37:44 +08001/*
2// Copyright (c) 2019 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 "filesystem.hpp"
18
19#include <PSUSensor.hpp>
20#include <Utils.hpp>
21#include <boost/algorithm/string/predicate.hpp>
22#include <boost/algorithm/string/replace.hpp>
23#include <boost/container/flat_set.hpp>
24#include <fstream>
25#include <regex>
26#include <sdbusplus/asio/connection.hpp>
27#include <sdbusplus/asio/object_server.hpp>
28
29static constexpr std::array<const char*, 1> sensorTypes = {
30 "xyz.openbmc_project.Configuration.pmbus"};
31
32namespace fs = std::filesystem;
33
34void createSensors(
35 boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
36 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
37 boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>>&
38 sensors,
Cheng C Yange50345b2019-04-02 17:26:15 +080039 boost::container::flat_map<std::string, std::string>& sensorTable,
40 boost::container::flat_map<std::string, PSUProperty>& labelMatch)
Cheng C Yang209ec562019-03-12 16:37:44 +080041{
42
43 ManagedObjectType sensorConfigs;
44 bool useCache = false;
45
46 for (const char* type : sensorTypes)
47 {
48 if (!getSensorConfiguration(type, dbusConnection, sensorConfigs,
49 useCache))
50 {
51 std::cerr << "error get sensor config from entity manager\n";
52 return;
53 }
54 useCache = true;
55 }
56
57 std::vector<fs::path> pmbusPaths;
58 if (!findFiles(fs::path("/sys/class/hwmon"), "name", pmbusPaths))
59 {
60 std::cerr << "No PSU sensors in system\n";
61 return;
62 }
63
64 boost::container::flat_set<std::string> directories;
65 for (const auto& pmbusPath : pmbusPaths)
66 {
67 const std::string pathStr = pmbusPath.string();
68 auto directory = pmbusPath.parent_path();
69
70 auto ret = directories.insert(directory.string());
71 if (!ret.second)
72 {
73 continue; // check if path i1 already searched
74 }
75
76 auto device = fs::path(directory / "device");
77 std::string deviceName = fs::canonical(device).stem();
78 auto findHyphen = deviceName.find("-");
79 if (findHyphen == std::string::npos)
80 {
81 std::cerr << "found bad device" << deviceName << "\n";
82 continue;
83 }
84 std::string busStr = deviceName.substr(0, findHyphen);
85 std::string addrStr = deviceName.substr(findHyphen + 1);
86
87 size_t bus = 0;
88 size_t addr = 0;
89
90 try
91 {
92 bus = std::stoi(busStr);
93 addr = std::stoi(addrStr, 0, 16);
94 }
95 catch (std::invalid_argument)
96 {
97 continue;
98 }
99
100 std::ifstream nameFile(pmbusPath);
101 if (!nameFile.good())
102 {
103 std::cerr << "Failure reading " << pmbusPath << "\n";
104 continue;
105 }
106
107 std::string pmbusName;
108 std::getline(nameFile, pmbusName);
109 nameFile.close();
110 if (pmbusName != "pmbus")
111 {
112 continue;
113 }
114
115 const std::pair<std::string, boost::container::flat_map<
116 std::string, BasicVariantType>>*
117 baseConfig = nullptr;
118 const SensorData* sensorData = nullptr;
119 const std::string* interfacePath = nullptr;
120 const char* sensorType = nullptr;
121
122 for (const std::pair<sdbusplus::message::object_path, SensorData>&
123 sensor : sensorConfigs)
124 {
125 sensorData = &(sensor.second);
126 for (const char* type : sensorTypes)
127 {
128 auto sensorBase = sensorData->find(type);
129 if (sensorBase != sensorData->end())
130 {
131 baseConfig = &(*sensorBase);
132 sensorType = type;
133 break;
134 }
135 }
136 if (baseConfig == nullptr)
137 {
138 std::cerr << "error finding base configuration for "
139 << deviceName << "\n";
140 continue;
141 }
142
143 auto configBus = baseConfig->second.find("Bus");
144 auto configAddress = baseConfig->second.find("Address");
145
146 if (configBus == baseConfig->second.end() ||
147 configAddress == baseConfig->second.end())
148 {
149 std::cerr << "error finding necessary entry in configuration";
150 continue;
151 }
152
153 if (std::get<uint64_t>(configBus->second) != bus ||
154 std::get<uint64_t>(configAddress->second) != addr)
155 {
156 continue;
157 }
158
159 interfacePath = &(sensor.first.str);
160 break;
161 }
162 if (interfacePath == nullptr)
163 {
164 std::cerr << "failed to find match for " << deviceName << "\n";
165 continue;
166 }
167
Cheng C Yange50345b2019-04-02 17:26:15 +0800168 auto findPSUName = baseConfig->second.find("Name");
169 if (findPSUName == baseConfig->second.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800170 {
171 std::cerr << "could not determine configuration name for "
172 << deviceName << "\n";
173 continue;
174 }
175
Cheng C Yange50345b2019-04-02 17:26:15 +0800176 std::vector<fs::path> sensorPaths;
177 if (!findFiles(fs::path(directory), R"(\w\d+_input$)", sensorPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800178 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800179 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800180 continue;
181 }
182
Cheng C Yange50345b2019-04-02 17:26:15 +0800183 for (const auto& sensorPath : sensorPaths)
Cheng C Yang209ec562019-03-12 16:37:44 +0800184 {
Cheng C Yang209ec562019-03-12 16:37:44 +0800185
Cheng C Yange50345b2019-04-02 17:26:15 +0800186 std::string labelHead;
187 std::string sensorPathStr = sensorPath.string();
188 std::string sensorNameStr = sensorPath.filename();
189 std::string sensorNameSubStr =
190 sensorNameStr.substr(0, sensorNameStr.find("_") - 1);
191
192 std::string labelPathStr =
193 boost::replace_all_copy(sensorNameStr, "input", "label");
194 std::vector<fs::path> labelPaths;
195 if (!findFiles(fs::path(directory), labelPathStr, labelPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800196 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800197 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800198 continue;
199 }
200
Cheng C Yange50345b2019-04-02 17:26:15 +0800201 if (labelPaths.empty())
Cheng C Yang209ec562019-03-12 16:37:44 +0800202 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800203 labelHead = sensorNameStr.substr(0, sensorNameStr.find("_"));
Cheng C Yang209ec562019-03-12 16:37:44 +0800204 }
205 else
206 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800207 auto labelPath =
208 boost::replace_all_copy(sensorPathStr, "input", "label");
209 std::ifstream labelFile(labelPath);
210 if (!labelFile.good())
211 {
212 std::cerr << "Failure reading " << sensorPath << "\n";
213 continue;
214 }
215 std::string label;
216 std::getline(labelFile, label);
217 labelFile.close();
Cheng C Yang209ec562019-03-12 16:37:44 +0800218
Cheng C Yange50345b2019-04-02 17:26:15 +0800219 auto findSensor = sensors.find(label);
220 if (findSensor != sensors.end())
221 {
222 continue;
223 }
224
225 labelHead = label.substr(0, label.find(" "));
226 }
227
228 std::vector<thresholds::Threshold> sensorThresholds;
229
230 parseThresholdsFromConfig(*sensorData, sensorThresholds,
231 &labelHead);
232
233 auto findProperty = labelMatch.find(labelHead);
234 if (findProperty == labelMatch.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800235 {
Cheng C Yang209ec562019-03-12 16:37:44 +0800236 continue;
237 }
238
Cheng C Yange50345b2019-04-02 17:26:15 +0800239 unsigned int factor =
240 std::pow(10, findProperty->second.sensorScaleFactor);
241 if (sensorThresholds.empty())
242 {
243 if (!parseThresholdsFromAttr(sensorThresholds, sensorPathStr,
244 factor))
245 {
246 std::cerr << "error populating thresholds\n";
247 }
248 }
249
250 auto findSensorType = sensorTable.find(sensorNameSubStr);
251 if (findSensorType == sensorTable.end())
252 {
253 std::cerr << "Cannot find PSU sensorType\n";
254 continue;
255 }
256
257 std::string sensorName =
258 std::get<std::string>(findPSUName->second) + " " +
259 findProperty->second.labelTypeName;
260
Cheng C Yang209ec562019-03-12 16:37:44 +0800261 sensors[sensorName] = std::make_unique<PSUSensor>(
Cheng C Yange50345b2019-04-02 17:26:15 +0800262 sensorPathStr, sensorType, objectServer, dbusConnection, io,
Cheng C Yang209ec562019-03-12 16:37:44 +0800263 sensorName, std::move(sensorThresholds), *interfacePath,
Cheng C Yange50345b2019-04-02 17:26:15 +0800264 findSensorType->second, factor, findProperty->second.maxReading,
265 findProperty->second.minReading);
Cheng C Yang209ec562019-03-12 16:37:44 +0800266 }
267 }
268 return;
269}
270
271void propertyInitialize(
Cheng C Yange50345b2019-04-02 17:26:15 +0800272 boost::container::flat_map<std::string, std::string>& sensorTable,
273 boost::container::flat_map<std::string, PSUProperty>& labelMatch)
Cheng C Yang209ec562019-03-12 16:37:44 +0800274{
Cheng C Yange50345b2019-04-02 17:26:15 +0800275 sensorTable = {{"power", "power/"},
276 {"curr", "current/"},
277 {"temp", "temperature/"},
278 {"in", "voltage/"},
279 {"fan", "fan_tach/"}};
280
281 labelMatch = {{"pin", PSUProperty("Input Power", 3000, 0, 6)},
282 {"pout1", PSUProperty("Output Power", 3000, 0, 6)},
283 {"vin", PSUProperty("Input Voltage", 255, 0, 3)},
284 {"iin", PSUProperty("Input Current", 20, 0, 3)},
285 {"iout1", PSUProperty("Output Current", 255, 0, 3)},
286 {"temp1", PSUProperty("Temperature", 127, -128, 3)},
287 {"fan1", PSUProperty("Fan Speed 1", 10000, 0, 0)},
288 {"fan2", PSUProperty("Fan Speed 2", 10000, 0, 0)}};
Cheng C Yang209ec562019-03-12 16:37:44 +0800289}
290
291int main(int argc, char** argv)
292{
293 boost::asio::io_service io;
294 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
295
296 systemBus->request_name("xyz.openbmc_project.PSUSensor");
297 sdbusplus::asio::object_server objectServer(systemBus);
298 boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>> sensors;
Cheng C Yange50345b2019-04-02 17:26:15 +0800299 boost::container::flat_map<std::string, std::string> sensorTable;
Cheng C Yang209ec562019-03-12 16:37:44 +0800300 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
Cheng C Yange50345b2019-04-02 17:26:15 +0800301 boost::container::flat_map<std::string, PSUProperty> labelMatch;
Cheng C Yang209ec562019-03-12 16:37:44 +0800302
303 propertyInitialize(sensorTable, labelMatch);
304
305 io.post([&]() {
306 createSensors(io, objectServer, systemBus, sensors, sensorTable,
307 labelMatch);
308 });
309 boost::asio::deadline_timer filterTimer(io);
310 std::function<void(sdbusplus::message::message&)> eventHandler =
311 [&](sdbusplus::message::message& message) {
312 if (message.is_method_error())
313 {
314 std::cerr << "callback method error\n";
315 return;
316 }
317 filterTimer.expires_from_now(boost::posix_time::seconds(1));
318 filterTimer.async_wait([&](const boost::system::error_code& ec) {
319 if (ec == boost::asio::error::operation_aborted)
320 {
321 return;
322 }
323 else if (ec)
324 {
325 std::cerr << "timer error\n";
326 }
327 createSensors(io, objectServer, systemBus, sensors, sensorTable,
328 labelMatch);
329 });
330 };
331
332 for (const char* type : sensorTypes)
333 {
334 auto match = std::make_unique<sdbusplus::bus::match::match>(
335 static_cast<sdbusplus::bus::bus&>(*systemBus),
336 "type='signal',member='PropertiesChanged',path_namespace='" +
337 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
338 eventHandler);
339 matches.emplace_back(std::move(match));
340 }
341 io.run();
342}