blob: 14d1a696331d2b261854cbdab5fcedf432405eeb [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
Cheng C Yang58b2b532019-05-31 00:19:45 +080017#include <PSUEvent.hpp>
Cheng C Yang209ec562019-03-12 16:37:44 +080018#include <PSUSensor.hpp>
19#include <Utils.hpp>
20#include <boost/algorithm/string/predicate.hpp>
21#include <boost/algorithm/string/replace.hpp>
22#include <boost/container/flat_set.hpp>
James Feist24f02f22019-04-15 11:05:39 -070023#include <filesystem>
Cheng C Yang209ec562019-03-12 16:37:44 +080024#include <fstream>
Cheng C Yang58b2b532019-05-31 00:19:45 +080025#include <iostream>
Cheng C Yang209ec562019-03-12 16:37:44 +080026#include <regex>
27#include <sdbusplus/asio/connection.hpp>
28#include <sdbusplus/asio/object_server.hpp>
29
30static constexpr std::array<const char*, 1> sensorTypes = {
31 "xyz.openbmc_project.Configuration.pmbus"};
32
33namespace fs = std::filesystem;
34
Cheng C Yang916360b2019-05-07 18:47:16 +080035static boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>>
36 sensors;
Cheng C Yang58b2b532019-05-31 00:19:45 +080037static boost::container::flat_map<std::string, std::unique_ptr<PSUCombineEvent>>
38 combineEvents;
Cheng C Yang916360b2019-05-07 18:47:16 +080039static boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
40 pwmSensors;
41static boost::container::flat_map<std::string, std::string> sensorTable;
42static boost::container::flat_map<std::string, PSUProperty> labelMatch;
43static boost::container::flat_map<std::string, std::string> pwmTable;
Cheng C Yang58b2b532019-05-31 00:19:45 +080044static boost::container::flat_map<std::string, std::vector<std::string>>
45 eventMatch;
46static boost::container::flat_map<std::string, std::vector<std::string>>
47 limitEventMatch;
48
49// Function CheckEvent will check each attribute from eventMatch table in the
50// sysfs. If the attributes exists in sysfs, then store the complete path
51// of the attribute into eventPathList.
52void checkEvent(
53 const std::string& directory,
54 const boost::container::flat_map<std::string, std::vector<std::string>>&
55 eventMatch,
56 boost::container::flat_map<std::string, std::vector<std::string>>&
57 eventPathList)
58{
59 for (const auto& match : eventMatch)
60 {
61 const std::vector<std::string>& eventAttrs = match.second;
62 const std::string& eventName = match.first;
63 for (const auto& eventAttr : eventAttrs)
64 {
65 auto eventPath = directory + "/" + eventAttr;
66
67 std::ifstream eventFile(eventPath);
68 if (!eventFile.good())
69 {
70 continue;
71 }
72
73 eventPathList[eventName].push_back(eventPath);
74 }
75 }
76}
77
78// Function checkEventLimits will check all the psu related xxx_input attributes
79// in sysfs to see if xxx_crit_alarm xxx_lcrit_alarm xxx_max_alarm
80// xxx_min_alarm exist, then store the existing paths of the alarm attributes
81// to eventPathList.
82void checkEventLimits(
83 const std::string& sensorPathStr,
84 const boost::container::flat_map<std::string, std::vector<std::string>>&
85 limitEventMatch,
86 boost::container::flat_map<std::string, std::vector<std::string>>&
87 eventPathList)
88{
89 for (const auto& limitMatch : limitEventMatch)
90 {
91 const std::vector<std::string>& limitEventAttrs = limitMatch.second;
92 const std::string& eventName = limitMatch.first;
93 for (const auto& limitEventAttr : limitEventAttrs)
94 {
95 auto limitEventPath =
96 boost::replace_all_copy(sensorPathStr, "input", limitEventAttr);
97 std::ifstream eventFile(limitEventPath);
98 if (!eventFile.good())
99 {
100 continue;
101 }
102 eventPathList[eventName].push_back(limitEventPath);
103 }
104 }
105}
Cheng C Yang916360b2019-05-07 18:47:16 +0800106
107static void checkPWMSensor(const fs::path& sensorPath, std::string& labelHead,
108 const std::string& interfacePath,
109 sdbusplus::asio::object_server& objectServer,
110 std::string psuName)
111{
112 for (const auto& pwmName : pwmTable)
113 {
114 if (pwmName.first != labelHead)
115 {
116 continue;
117 }
118
119 const std::string& sensorPathStr = sensorPath.string();
120 const std::string& pwmPathStr =
121 boost::replace_all_copy(sensorPathStr, "input", "target");
122 std::ifstream pwmFile(pwmPathStr);
123 if (!pwmFile.good())
124 {
125 continue;
126 }
127
128 auto findPWMSensor = pwmSensors.find(psuName + labelHead);
129 if (findPWMSensor != pwmSensors.end())
130 {
131 continue;
132 }
133
134 pwmSensors[psuName + labelHead] = std::make_unique<PwmSensor>(
135 "Pwm_" + psuName + "_" + pwmName.second, pwmPathStr, objectServer,
136 interfacePath + "/" + psuName + " " + pwmName.second);
137 }
138}
139
140void createSensors(boost::asio::io_service& io,
141 sdbusplus::asio::object_server& objectServer,
142 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
Cheng C Yang209ec562019-03-12 16:37:44 +0800143{
144
145 ManagedObjectType sensorConfigs;
146 bool useCache = false;
147
Cheng C Yang58b2b532019-05-31 00:19:45 +0800148 // TODO may need only modify the ones that need to be changed.
149 sensors.clear();
Cheng C Yang209ec562019-03-12 16:37:44 +0800150 for (const char* type : sensorTypes)
151 {
152 if (!getSensorConfiguration(type, dbusConnection, sensorConfigs,
153 useCache))
154 {
155 std::cerr << "error get sensor config from entity manager\n";
156 return;
157 }
158 useCache = true;
159 }
160
161 std::vector<fs::path> pmbusPaths;
162 if (!findFiles(fs::path("/sys/class/hwmon"), "name", pmbusPaths))
163 {
164 std::cerr << "No PSU sensors in system\n";
165 return;
166 }
167
168 boost::container::flat_set<std::string> directories;
169 for (const auto& pmbusPath : pmbusPaths)
170 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800171 boost::container::flat_map<std::string, std::vector<std::string>>
172 eventPathList;
173
174 std::ifstream nameFile(pmbusPath);
175 if (!nameFile.good())
176 {
177 std::cerr << "Failure reading " << pmbusPath << "\n";
178 continue;
179 }
180
181 std::string pmbusName;
182 std::getline(nameFile, pmbusName);
183 nameFile.close();
184 if (pmbusName != "pmbus")
185 {
186 continue;
187 }
188
189 const std::string* psuName;
Cheng C Yang209ec562019-03-12 16:37:44 +0800190 auto directory = pmbusPath.parent_path();
191
192 auto ret = directories.insert(directory.string());
193 if (!ret.second)
194 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800195 continue; // check if path has already been searched
Cheng C Yang209ec562019-03-12 16:37:44 +0800196 }
197
198 auto device = fs::path(directory / "device");
199 std::string deviceName = fs::canonical(device).stem();
200 auto findHyphen = deviceName.find("-");
201 if (findHyphen == std::string::npos)
202 {
203 std::cerr << "found bad device" << deviceName << "\n";
204 continue;
205 }
206 std::string busStr = deviceName.substr(0, findHyphen);
207 std::string addrStr = deviceName.substr(findHyphen + 1);
208
209 size_t bus = 0;
210 size_t addr = 0;
211
212 try
213 {
214 bus = std::stoi(busStr);
215 addr = std::stoi(addrStr, 0, 16);
216 }
217 catch (std::invalid_argument)
218 {
219 continue;
220 }
221
Cheng C Yang209ec562019-03-12 16:37:44 +0800222 const std::pair<std::string, boost::container::flat_map<
223 std::string, BasicVariantType>>*
224 baseConfig = nullptr;
225 const SensorData* sensorData = nullptr;
226 const std::string* interfacePath = nullptr;
227 const char* sensorType = nullptr;
228
229 for (const std::pair<sdbusplus::message::object_path, SensorData>&
230 sensor : sensorConfigs)
231 {
232 sensorData = &(sensor.second);
233 for (const char* type : sensorTypes)
234 {
235 auto sensorBase = sensorData->find(type);
236 if (sensorBase != sensorData->end())
237 {
238 baseConfig = &(*sensorBase);
239 sensorType = type;
240 break;
241 }
242 }
243 if (baseConfig == nullptr)
244 {
245 std::cerr << "error finding base configuration for "
246 << deviceName << "\n";
247 continue;
248 }
249
250 auto configBus = baseConfig->second.find("Bus");
251 auto configAddress = baseConfig->second.find("Address");
252
253 if (configBus == baseConfig->second.end() ||
254 configAddress == baseConfig->second.end())
255 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800256 std::cerr << "error finding necessary entry in configuration\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800257 continue;
258 }
259
Cheng C Yang58b2b532019-05-31 00:19:45 +0800260 const uint64_t* confBus;
261 const uint64_t* confAddr;
262 if (!(confBus = std::get_if<uint64_t>(&(configBus->second))) ||
263 !(confAddr = std::get_if<uint64_t>(&(configAddress->second))))
264 {
265 std::cerr
266 << "Canot get bus or address, invalid configuration\n";
267 continue;
268 }
269
270 if ((*confBus != bus) || (*confAddr != addr))
Cheng C Yang209ec562019-03-12 16:37:44 +0800271 {
272 continue;
273 }
274
275 interfacePath = &(sensor.first.str);
276 break;
277 }
278 if (interfacePath == nullptr)
279 {
280 std::cerr << "failed to find match for " << deviceName << "\n";
281 continue;
282 }
283
Cheng C Yange50345b2019-04-02 17:26:15 +0800284 auto findPSUName = baseConfig->second.find("Name");
285 if (findPSUName == baseConfig->second.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800286 {
287 std::cerr << "could not determine configuration name for "
288 << deviceName << "\n";
289 continue;
290 }
291
Cheng C Yang58b2b532019-05-31 00:19:45 +0800292 if (!(psuName = std::get_if<std::string>(&(findPSUName->second))))
293 {
294 std::cerr << "Cannot find psu name, invalid configuration\n";
295 continue;
296 }
297 checkEvent(directory.string(), eventMatch, eventPathList);
298
Cheng C Yange50345b2019-04-02 17:26:15 +0800299 std::vector<fs::path> sensorPaths;
300 if (!findFiles(fs::path(directory), R"(\w\d+_input$)", sensorPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800301 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800302 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800303 continue;
304 }
305
Cheng C Yange50345b2019-04-02 17:26:15 +0800306 for (const auto& sensorPath : sensorPaths)
Cheng C Yang209ec562019-03-12 16:37:44 +0800307 {
Cheng C Yang209ec562019-03-12 16:37:44 +0800308
Cheng C Yange50345b2019-04-02 17:26:15 +0800309 std::string labelHead;
310 std::string sensorPathStr = sensorPath.string();
311 std::string sensorNameStr = sensorPath.filename();
312 std::string sensorNameSubStr =
313 sensorNameStr.substr(0, sensorNameStr.find("_") - 1);
314
315 std::string labelPathStr =
316 boost::replace_all_copy(sensorNameStr, "input", "label");
317 std::vector<fs::path> labelPaths;
318 if (!findFiles(fs::path(directory), labelPathStr, labelPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800319 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800320 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800321 continue;
322 }
323
Cheng C Yange50345b2019-04-02 17:26:15 +0800324 if (labelPaths.empty())
Cheng C Yang209ec562019-03-12 16:37:44 +0800325 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800326 labelHead = sensorNameStr.substr(0, sensorNameStr.find("_"));
Cheng C Yang209ec562019-03-12 16:37:44 +0800327 }
328 else
329 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800330 auto labelPath =
331 boost::replace_all_copy(sensorPathStr, "input", "label");
332 std::ifstream labelFile(labelPath);
333 if (!labelFile.good())
334 {
335 std::cerr << "Failure reading " << sensorPath << "\n";
336 continue;
337 }
338 std::string label;
339 std::getline(labelFile, label);
340 labelFile.close();
Cheng C Yang209ec562019-03-12 16:37:44 +0800341
Cheng C Yange50345b2019-04-02 17:26:15 +0800342 auto findSensor = sensors.find(label);
343 if (findSensor != sensors.end())
344 {
345 continue;
346 }
347
348 labelHead = label.substr(0, label.find(" "));
349 }
350
Cheng C Yang916360b2019-05-07 18:47:16 +0800351 checkPWMSensor(sensorPath, labelHead, *interfacePath, objectServer,
352 std::get<std::string>(findPSUName->second));
353
Cheng C Yange50345b2019-04-02 17:26:15 +0800354 std::vector<thresholds::Threshold> sensorThresholds;
355
356 parseThresholdsFromConfig(*sensorData, sensorThresholds,
357 &labelHead);
358
359 auto findProperty = labelMatch.find(labelHead);
360 if (findProperty == labelMatch.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800361 {
Cheng C Yang209ec562019-03-12 16:37:44 +0800362 continue;
363 }
364
Cheng C Yang58b2b532019-05-31 00:19:45 +0800365 checkEventLimits(sensorPathStr, limitEventMatch, eventPathList);
366
Cheng C Yange50345b2019-04-02 17:26:15 +0800367 unsigned int factor =
368 std::pow(10, findProperty->second.sensorScaleFactor);
369 if (sensorThresholds.empty())
370 {
371 if (!parseThresholdsFromAttr(sensorThresholds, sensorPathStr,
372 factor))
373 {
374 std::cerr << "error populating thresholds\n";
375 }
376 }
377
378 auto findSensorType = sensorTable.find(sensorNameSubStr);
379 if (findSensorType == sensorTable.end())
380 {
381 std::cerr << "Cannot find PSU sensorType\n";
382 continue;
383 }
384
385 std::string sensorName =
Cheng C Yang58b2b532019-05-31 00:19:45 +0800386 *psuName + " " + findProperty->second.labelTypeName;
Cheng C Yange50345b2019-04-02 17:26:15 +0800387
Cheng C Yang58b2b532019-05-31 00:19:45 +0800388 sensors[sensorName] = std::make_unique<PSUSensor>(
Cheng C Yange50345b2019-04-02 17:26:15 +0800389 sensorPathStr, sensorType, objectServer, dbusConnection, io,
Cheng C Yang209ec562019-03-12 16:37:44 +0800390 sensorName, std::move(sensorThresholds), *interfacePath,
Cheng C Yange50345b2019-04-02 17:26:15 +0800391 findSensorType->second, factor, findProperty->second.maxReading,
392 findProperty->second.minReading);
Cheng C Yang209ec562019-03-12 16:37:44 +0800393 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800394
395 // OperationalStatus event
396 combineEvents[*psuName + "OperationalStatus"] =
397 std::make_unique<PSUCombineEvent>(
398 objectServer, io, *psuName, eventPathList, "OperationalStatus");
Cheng C Yang209ec562019-03-12 16:37:44 +0800399 }
400 return;
401}
402
Cheng C Yang916360b2019-05-07 18:47:16 +0800403void propertyInitialize(void)
Cheng C Yang209ec562019-03-12 16:37:44 +0800404{
Cheng C Yange50345b2019-04-02 17:26:15 +0800405 sensorTable = {{"power", "power/"},
406 {"curr", "current/"},
407 {"temp", "temperature/"},
408 {"in", "voltage/"},
409 {"fan", "fan_tach/"}};
410
411 labelMatch = {{"pin", PSUProperty("Input Power", 3000, 0, 6)},
412 {"pout1", PSUProperty("Output Power", 3000, 0, 6)},
Cheng C Yangbdbde962019-04-26 22:50:34 +0800413 {"vin", PSUProperty("Input Voltage", 300, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800414 {"iin", PSUProperty("Input Current", 20, 0, 3)},
415 {"iout1", PSUProperty("Output Current", 255, 0, 3)},
416 {"temp1", PSUProperty("Temperature", 127, -128, 3)},
Cheng C Yang8dbb3952019-05-23 00:03:12 +0800417 {"fan1", PSUProperty("Fan Speed 1", 30000, 0, 0)},
418 {"fan2", PSUProperty("Fan Speed 2", 30000, 0, 0)}};
Cheng C Yang916360b2019-05-07 18:47:16 +0800419
420 pwmTable = {{"fan1", "Fan_1"}, {"fan2", "Fan_2"}};
Cheng C Yang58b2b532019-05-31 00:19:45 +0800421
422 limitEventMatch = {{"PredictiveFailure", {"max_alarm", "min_alarm"}},
423 {"Failure", {"crit_alarm", "lcrit_alarm"}}};
424
425 eventMatch = {
426 {"PredictiveFailure", {"power1_alarm"}},
427 {"Failure", {"in2_alarm"}},
428 {"ACLost", {"in1_alarm", "in1_lcrit_alarm"}},
429 {"FanFault", {"fan1_alarm", "fan2_alarm", "fan1_fault", "fan2_fault"}}};
Cheng C Yang209ec562019-03-12 16:37:44 +0800430}
431
432int main(int argc, char** argv)
433{
434 boost::asio::io_service io;
435 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
436
437 systemBus->request_name("xyz.openbmc_project.PSUSensor");
438 sdbusplus::asio::object_server objectServer(systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800439 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
Cheng C Yang209ec562019-03-12 16:37:44 +0800440
Cheng C Yang916360b2019-05-07 18:47:16 +0800441 propertyInitialize();
Cheng C Yang209ec562019-03-12 16:37:44 +0800442
Cheng C Yang916360b2019-05-07 18:47:16 +0800443 io.post([&]() { createSensors(io, objectServer, systemBus); });
Cheng C Yang209ec562019-03-12 16:37:44 +0800444 boost::asio::deadline_timer filterTimer(io);
445 std::function<void(sdbusplus::message::message&)> eventHandler =
446 [&](sdbusplus::message::message& message) {
447 if (message.is_method_error())
448 {
449 std::cerr << "callback method error\n";
450 return;
451 }
452 filterTimer.expires_from_now(boost::posix_time::seconds(1));
453 filterTimer.async_wait([&](const boost::system::error_code& ec) {
454 if (ec == boost::asio::error::operation_aborted)
455 {
456 return;
457 }
458 else if (ec)
459 {
460 std::cerr << "timer error\n";
461 }
Cheng C Yang916360b2019-05-07 18:47:16 +0800462 createSensors(io, objectServer, systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800463 });
464 };
465
466 for (const char* type : sensorTypes)
467 {
468 auto match = std::make_unique<sdbusplus::bus::match::match>(
469 static_cast<sdbusplus::bus::bus&>(*systemBus),
470 "type='signal',member='PropertiesChanged',path_namespace='" +
471 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
472 eventHandler);
473 matches.emplace_back(std::move(match));
474 }
475 io.run();
476}