blob: c752cc6ed5a9204c6f44c0fddfd8987d0e031e26 [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,
39 boost::container::flat_map<SensorType, std::unique_ptr<PSUProperty>>&
40 sensorTable,
41 boost::container::flat_map<std::string, std::string>& labelMatch)
42{
43
44 ManagedObjectType sensorConfigs;
45 bool useCache = false;
46
47 for (const char* type : sensorTypes)
48 {
49 if (!getSensorConfiguration(type, dbusConnection, sensorConfigs,
50 useCache))
51 {
52 std::cerr << "error get sensor config from entity manager\n";
53 return;
54 }
55 useCache = true;
56 }
57
58 std::vector<fs::path> pmbusPaths;
59 if (!findFiles(fs::path("/sys/class/hwmon"), "name", pmbusPaths))
60 {
61 std::cerr << "No PSU sensors in system\n";
62 return;
63 }
64
65 boost::container::flat_set<std::string> directories;
66 for (const auto& pmbusPath : pmbusPaths)
67 {
68 const std::string pathStr = pmbusPath.string();
69 auto directory = pmbusPath.parent_path();
70
71 auto ret = directories.insert(directory.string());
72 if (!ret.second)
73 {
74 continue; // check if path i1 already searched
75 }
76
77 auto device = fs::path(directory / "device");
78 std::string deviceName = fs::canonical(device).stem();
79 auto findHyphen = deviceName.find("-");
80 if (findHyphen == std::string::npos)
81 {
82 std::cerr << "found bad device" << deviceName << "\n";
83 continue;
84 }
85 std::string busStr = deviceName.substr(0, findHyphen);
86 std::string addrStr = deviceName.substr(findHyphen + 1);
87
88 size_t bus = 0;
89 size_t addr = 0;
90
91 try
92 {
93 bus = std::stoi(busStr);
94 addr = std::stoi(addrStr, 0, 16);
95 }
96 catch (std::invalid_argument)
97 {
98 continue;
99 }
100
101 std::ifstream nameFile(pmbusPath);
102 if (!nameFile.good())
103 {
104 std::cerr << "Failure reading " << pmbusPath << "\n";
105 continue;
106 }
107
108 std::string pmbusName;
109 std::getline(nameFile, pmbusName);
110 nameFile.close();
111 if (pmbusName != "pmbus")
112 {
113 continue;
114 }
115
116 const std::pair<std::string, boost::container::flat_map<
117 std::string, BasicVariantType>>*
118 baseConfig = nullptr;
119 const SensorData* sensorData = nullptr;
120 const std::string* interfacePath = nullptr;
121 const char* sensorType = nullptr;
122
123 for (const std::pair<sdbusplus::message::object_path, SensorData>&
124 sensor : sensorConfigs)
125 {
126 sensorData = &(sensor.second);
127 for (const char* type : sensorTypes)
128 {
129 auto sensorBase = sensorData->find(type);
130 if (sensorBase != sensorData->end())
131 {
132 baseConfig = &(*sensorBase);
133 sensorType = type;
134 break;
135 }
136 }
137 if (baseConfig == nullptr)
138 {
139 std::cerr << "error finding base configuration for "
140 << deviceName << "\n";
141 continue;
142 }
143
144 auto configBus = baseConfig->second.find("Bus");
145 auto configAddress = baseConfig->second.find("Address");
146
147 if (configBus == baseConfig->second.end() ||
148 configAddress == baseConfig->second.end())
149 {
150 std::cerr << "error finding necessary entry in configuration";
151 continue;
152 }
153
154 if (std::get<uint64_t>(configBus->second) != bus ||
155 std::get<uint64_t>(configAddress->second) != addr)
156 {
157 continue;
158 }
159
160 interfacePath = &(sensor.first.str);
161 break;
162 }
163 if (interfacePath == nullptr)
164 {
165 std::cerr << "failed to find match for " << deviceName << "\n";
166 continue;
167 }
168
169 auto findSensorName = baseConfig->second.find("Name");
170 if (findSensorName == baseConfig->second.end())
171 {
172 std::cerr << "could not determine configuration name for "
173 << deviceName << "\n";
174 continue;
175 }
176
177 std::vector<fs::path> powerPaths;
178 if (!findFiles(fs::path(directory), R"(power\d+_input$)", powerPaths,
179 0))
180 {
181 std::cerr << "No power sensor in PSU\n";
182 continue;
183 }
184
185 for (const auto& powerPath : powerPaths)
186 {
187 auto powerPathStr = powerPath.string();
188 auto labelPath =
189 boost::replace_all_copy(powerPathStr, "input", "label");
190 std::ifstream labelFile(labelPath);
191 if (!labelFile.good())
192 {
193 std::cerr << "Failure reading " << powerPath << "\n";
194 continue;
195 }
196 std::string label;
197 std::getline(labelFile, label);
198 labelFile.close();
199
200 auto findSensor = sensors.find(label);
201 if (findSensor != sensors.end())
202 {
203 continue;
204 }
205
206 std::vector<thresholds::Threshold> sensorThresholds;
207 std::string labelHead = label.substr(0, label.find(" "));
208 parseThresholdsFromConfig(*sensorData, sensorThresholds,
209 &labelHead);
210 if (sensorThresholds.empty())
211 {
212 continue;
213 }
214
215 std::string labelName;
216 auto findLabel = labelMatch.find(label);
217 if (findLabel != labelMatch.end())
218 {
219 labelName = findLabel->second;
220 }
221 else
222 {
223 labelName = label;
224 }
225 std::string sensorName =
226 std::get<std::string>(findSensorName->second) + " " + labelName;
227
228 auto findProperty = sensorTable.find(SensorType::powerSensor);
229 if (findProperty == sensorTable.end())
230 {
231 std::cerr << "Cannot find PSU sensorType " << sensorType
232 << "\n";
233 continue;
234 }
235
236 sensors[sensorName] = std::make_unique<PSUSensor>(
237 powerPathStr, sensorType, objectServer, dbusConnection, io,
238 sensorName, std::move(sensorThresholds), *interfacePath,
239 findProperty->second->sensorTypeName,
240 findProperty->second->sensorScaleFactor,
241 findProperty->second->maxReading,
242 findProperty->second->minReading);
243 }
244 }
245 return;
246}
247
248void propertyInitialize(
249 boost::container::flat_map<SensorType, std::unique_ptr<PSUProperty>>&
250 sensorTable,
251 boost::container::flat_map<std::string, std::string>& labelMatch)
252{
253 sensorTable[SensorType::powerSensor] =
254 std::make_unique<PSUProperty>("power/", 65535, 0, 100000);
255 labelMatch["pin"] = "Input Power";
256}
257
258int main(int argc, char** argv)
259{
260 boost::asio::io_service io;
261 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
262
263 systemBus->request_name("xyz.openbmc_project.PSUSensor");
264 sdbusplus::asio::object_server objectServer(systemBus);
265 boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>> sensors;
266 boost::container::flat_map<SensorType, std::unique_ptr<PSUProperty>>
267 sensorTable;
268 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
269 boost::container::flat_map<std::string, std::string> labelMatch;
270
271 propertyInitialize(sensorTable, labelMatch);
272
273 io.post([&]() {
274 createSensors(io, objectServer, systemBus, sensors, sensorTable,
275 labelMatch);
276 });
277 boost::asio::deadline_timer filterTimer(io);
278 std::function<void(sdbusplus::message::message&)> eventHandler =
279 [&](sdbusplus::message::message& message) {
280 if (message.is_method_error())
281 {
282 std::cerr << "callback method error\n";
283 return;
284 }
285 filterTimer.expires_from_now(boost::posix_time::seconds(1));
286 filterTimer.async_wait([&](const boost::system::error_code& ec) {
287 if (ec == boost::asio::error::operation_aborted)
288 {
289 return;
290 }
291 else if (ec)
292 {
293 std::cerr << "timer error\n";
294 }
295 createSensors(io, objectServer, systemBus, sensors, sensorTable,
296 labelMatch);
297 });
298 };
299
300 for (const char* type : sensorTypes)
301 {
302 auto match = std::make_unique<sdbusplus::bus::match::match>(
303 static_cast<sdbusplus::bus::bus&>(*systemBus),
304 "type='signal',member='PropertiesChanged',path_namespace='" +
305 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
306 eventHandler);
307 matches.emplace_back(std::move(match));
308 }
309 io.run();
310}