blob: 899ec981ae1c9b714d2e89c7f88009a8434a42f9 [file] [log] [blame]
James Feist6714a252018-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
Patrick Ventureca44b2f2019-10-31 11:02:26 -070017#include "Utils.hpp"
18
James Feist6714a252018-09-10 15:26:18 -070019#include <boost/algorithm/string/predicate.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070020#include <boost/container/flat_map.hpp>
James Feist38fb5982020-05-28 10:09:54 -070021#include <sdbusplus/asio/connection.hpp>
22#include <sdbusplus/asio/object_server.hpp>
23#include <sdbusplus/bus/match.hpp>
24
James Feist24f02f22019-04-15 11:05:39 -070025#include <filesystem>
James Feist6714a252018-09-10 15:26:18 -070026#include <fstream>
Patrick Venture96e97db2019-10-31 13:44:38 -070027#include <memory>
James Feist6714a252018-09-10 15:26:18 -070028#include <regex>
Patrick Venture96e97db2019-10-31 13:44:38 -070029#include <stdexcept>
30#include <string>
31#include <utility>
32#include <variant>
33#include <vector>
James Feist6714a252018-09-10 15:26:18 -070034
James Feistcf3bce62019-01-08 10:07:19 -080035namespace fs = std::filesystem;
James Feist6714a252018-09-10 15:26:18 -070036
James Feist6ef20402019-01-07 16:45:08 -080037static bool powerStatusOn = false;
James Feistfc94b212019-02-06 16:14:51 -080038static bool biosHasPost = false;
James Feist58295ad2019-05-30 15:01:41 -070039
James Feist6ef20402019-01-07 16:45:08 -080040static std::unique_ptr<sdbusplus::bus::match::match> powerMatch = nullptr;
James Feist52497fd2019-06-07 13:01:33 -070041static std::unique_ptr<sdbusplus::bus::match::match> postMatch = nullptr;
James Feist71d31b22019-01-02 16:57:54 -080042
Jason Ling100c20b2020-08-11 14:50:33 -070043/**
44 * return the contents of a file
45 * @param[in] hwmonFile - the path to the file to read
46 * @return the contents of the file as a string or nullopt if the file could not
47 * be opened.
48 */
49
50std::optional<std::string> openAndRead(const std::string& hwmonFile)
51{
52 std::string fileVal;
53 std::ifstream fileStream(hwmonFile);
54 if (!fileStream.is_open())
55 {
56 return std::nullopt;
57 }
58 std::getline(fileStream, fileVal);
59 return fileVal;
60}
61
62/**
63 * given a hwmon temperature base name if valid return the full path else
64 * nullopt
65 * @param[in] directory - the hwmon sysfs directory
66 * @param[in] permitSet - a set of labels or hwmon basenames to permit. If this
67 * is empty then *everything* is permitted.
68 * @return a string to the full path of the file to create a temp sensor with or
69 * nullopt to indicate that no sensor should be created for this basename.
70 */
71std::optional<std::string>
72 getFullHwmonFilePath(const std::string& directory,
73 const std::string& hwmonBaseName,
74 const std::set<std::string>& permitSet)
75{
76 std::optional<std::string> result;
77 std::string filename;
78 if (permitSet.empty())
79 {
80 result = directory + "/" + hwmonBaseName + "_input";
81 return result;
82 }
83 filename = directory + "/" + hwmonBaseName + "_label";
84 auto searchVal = openAndRead(filename);
85 if (!searchVal)
86 {
87 /* if the hwmon temp doesn't have a corresponding label file
88 * then use the hwmon temperature base name
89 */
90 searchVal = hwmonBaseName;
91 }
92 if (permitSet.find(*searchVal) != permitSet.end())
93 {
94 result = directory + "/" + hwmonBaseName + "_input";
95 }
96 return result;
97}
98
99/**
100 * retrieve a set of basenames and labels to allow sensor creation for.
101 * @param[in] config - a map representing the configuration for a specific
102 * device
103 * @return a set of basenames and labels to allow sensor creation for. An empty
104 * set indicates that everything is permitted.
105 */
106std::set<std::string> getPermitSet(const SensorBaseConfigMap& config)
107{
108 auto permitAttribute = config.find("Labels");
109 std::set<std::string> permitSet;
110 if (permitAttribute != config.end())
111 {
112 try
113 {
114 auto val =
115 std::get<std::vector<std::string>>(permitAttribute->second);
116
117 permitSet.insert(std::make_move_iterator(val.begin()),
118 std::make_move_iterator(val.end()));
119 }
120 catch (const std::bad_variant_access& err)
121 {
122 std::cerr << err.what()
123 << ":PermitList does not contain a list, wrong "
124 "variant type.\n";
125 }
126 }
127 return permitSet;
128}
129
James Feist6714a252018-09-10 15:26:18 -0700130bool getSensorConfiguration(
131 const std::string& type,
132 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
133 ManagedObjectType& resp, bool useCache)
134{
135 static ManagedObjectType managedObj;
136
137 if (!useCache)
138 {
139 managedObj.clear();
140 sdbusplus::message::message getManagedObjects =
141 dbusConnection->new_method_call(
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700142 entityManagerName, "/", "org.freedesktop.DBus.ObjectManager",
James Feist6714a252018-09-10 15:26:18 -0700143 "GetManagedObjects");
144 bool err = false;
145 try
146 {
147 sdbusplus::message::message reply =
148 dbusConnection->call(getManagedObjects);
Yoo, Jae Hyun0e022052018-10-15 14:05:37 -0700149 reply.read(managedObj);
James Feist6714a252018-09-10 15:26:18 -0700150 }
Jason Ling5747fab2019-10-02 16:46:23 -0700151 catch (const sdbusplus::exception::exception& e)
James Feist6714a252018-09-10 15:26:18 -0700152 {
Jason Ling5747fab2019-10-02 16:46:23 -0700153 std::cerr << "While calling GetManagedObjects on service:"
154 << entityManagerName << " exception name:" << e.name()
155 << "and description:" << e.description()
156 << " was thrown\n";
James Feist6714a252018-09-10 15:26:18 -0700157 err = true;
158 }
159
160 if (err)
161 {
162 std::cerr << "Error communicating to entity manager\n";
163 return false;
164 }
165 }
166 for (const auto& pathPair : managedObj)
167 {
James Feist6714a252018-09-10 15:26:18 -0700168 bool correctType = false;
169 for (const auto& entry : pathPair.second)
170 {
171 if (boost::starts_with(entry.first, type))
172 {
173 correctType = true;
174 break;
175 }
176 }
177 if (correctType)
178 {
179 resp.emplace(pathPair);
180 }
181 }
182 return true;
183}
184
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700185bool findFiles(const fs::path dirPath, const std::string& matchString,
186 std::vector<fs::path>& foundPaths, unsigned int symlinkDepth)
James Feist6714a252018-09-10 15:26:18 -0700187{
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700188 if (!fs::exists(dirPath))
James Feist6714a252018-09-10 15:26:18 -0700189 return false;
190
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700191 std::regex search(matchString);
James Feist6714a252018-09-10 15:26:18 -0700192 std::smatch match;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700193 for (auto& p : fs::recursive_directory_iterator(dirPath))
James Feist6714a252018-09-10 15:26:18 -0700194 {
195 std::string path = p.path().string();
196 if (!is_directory(p))
197 {
198 if (std::regex_search(path, match, search))
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700199 foundPaths.emplace_back(p.path());
James Feist6714a252018-09-10 15:26:18 -0700200 }
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700201 else if (is_symlink(p) && symlinkDepth)
James Feist6714a252018-09-10 15:26:18 -0700202 {
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700203 findFiles(p.path(), matchString, foundPaths, symlinkDepth - 1);
James Feist6714a252018-09-10 15:26:18 -0700204 }
205 }
206 return true;
207}
208
James Feist71d31b22019-01-02 16:57:54 -0800209bool isPowerOn(void)
James Feist6714a252018-09-10 15:26:18 -0700210{
James Feist71d31b22019-01-02 16:57:54 -0800211 if (!powerMatch)
James Feist6714a252018-09-10 15:26:18 -0700212 {
James Feist71d31b22019-01-02 16:57:54 -0800213 throw std::runtime_error("Power Match Not Created");
James Feist6714a252018-09-10 15:26:18 -0700214 }
James Feist71d31b22019-01-02 16:57:54 -0800215 return powerStatusOn;
216}
217
James Feistfc94b212019-02-06 16:14:51 -0800218bool hasBiosPost(void)
219{
James Feist52497fd2019-06-07 13:01:33 -0700220 if (!postMatch)
James Feistfc94b212019-02-06 16:14:51 -0800221 {
James Feist52497fd2019-06-07 13:01:33 -0700222 throw std::runtime_error("Post Match Not Created");
James Feistfc94b212019-02-06 16:14:51 -0800223 }
224 return biosHasPost;
225}
226
James Feist8aeffd92020-09-14 12:08:28 -0700227static void
228 getPowerStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
229 size_t retries = 2)
230{
231 conn->async_method_call(
232 [conn, retries](boost::system::error_code ec,
233 const std::variant<std::string>& state) {
234 if (ec)
235 {
236 if (retries)
237 {
238 auto timer = std::make_shared<boost::asio::steady_timer>(
239 conn->get_io_context());
240 timer->expires_after(std::chrono::seconds(15));
241 timer->async_wait(
242 [timer, conn, retries](boost::system::error_code) {
243 getPowerStatus(conn, retries - 1);
244 });
245 return;
246 }
247
248 // we commonly come up before power control, we'll capture the
249 // property change later
250 std::cerr << "error getting power status " << ec.message()
251 << "\n";
252 return;
253 }
254 powerStatusOn =
255 boost::ends_with(std::get<std::string>(state), "Running");
256 },
257 power::busname, power::path, properties::interface, properties::get,
258 power::interface, power::property);
259}
260
261static void
262 getPostStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
263 size_t retries = 2)
264{
265 conn->async_method_call(
266 [conn, retries](boost::system::error_code ec,
267 const std::variant<std::string>& state) {
268 if (ec)
269 {
270 if (retries)
271 {
272 auto timer = std::make_shared<boost::asio::steady_timer>(
273 conn->get_io_context());
274 timer->expires_after(std::chrono::seconds(15));
275 timer->async_wait(
276 [timer, conn, retries](boost::system::error_code) {
277 getPostStatus(conn, retries - 1);
278 });
279 return;
280 }
281 // we commonly come up before power control, we'll capture the
282 // property change later
283 std::cerr << "error getting post status " << ec.message()
284 << "\n";
285 return;
286 }
287 biosHasPost = std::get<std::string>(state) != "Inactive";
288 },
289 post::busname, post::path, properties::interface, properties::get,
290 post::interface, post::property);
291}
292
James Feist71d31b22019-01-02 16:57:54 -0800293void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
294{
James Feist43d32fe2019-09-04 10:35:20 -0700295 static boost::asio::steady_timer timer(conn->get_io_context());
James Feist6714a252018-09-10 15:26:18 -0700296 // create a match for powergood changes, first time do a method call to
James Feist71d31b22019-01-02 16:57:54 -0800297 // cache the correct value
James Feist52497fd2019-06-07 13:01:33 -0700298 if (powerMatch)
299 {
300 return;
301 }
James Feist6714a252018-09-10 15:26:18 -0700302
303 powerMatch = std::make_unique<sdbusplus::bus::match::match>(
304 static_cast<sdbusplus::bus::bus&>(*conn),
James Feist52497fd2019-06-07 13:01:33 -0700305 "type='signal',interface='" + std::string(properties::interface) +
306 "',path='" + std::string(power::path) + "',arg0='" +
307 std::string(power::interface) + "'",
308 [](sdbusplus::message::message& message) {
309 std::string objectName;
310 boost::container::flat_map<std::string, std::variant<std::string>>
311 values;
312 message.read(objectName, values);
313 auto findState = values.find(power::property);
314 if (findState != values.end())
James Feist6714a252018-09-10 15:26:18 -0700315 {
James Feist43d32fe2019-09-04 10:35:20 -0700316 bool on = boost::ends_with(
James Feist52497fd2019-06-07 13:01:33 -0700317 std::get<std::string>(findState->second), "Running");
James Feist43d32fe2019-09-04 10:35:20 -0700318 if (!on)
319 {
320 timer.cancel();
321 powerStatusOn = false;
322 return;
323 }
324 // on comes too quickly
325 timer.expires_after(std::chrono::seconds(10));
326 timer.async_wait([](boost::system::error_code ec) {
327 if (ec == boost::asio::error::operation_aborted)
328 {
329 return;
330 }
331 else if (ec)
332 {
333 std::cerr << "Timer error " << ec.message() << "\n";
334 return;
335 }
336 powerStatusOn = true;
337 });
James Feist6714a252018-09-10 15:26:18 -0700338 }
James Feist52497fd2019-06-07 13:01:33 -0700339 });
340
341 postMatch = std::make_unique<sdbusplus::bus::match::match>(
342 static_cast<sdbusplus::bus::bus&>(*conn),
343 "type='signal',interface='" + std::string(properties::interface) +
344 "',path='" + std::string(post::path) + "',arg0='" +
345 std::string(post::interface) + "'",
346 [](sdbusplus::message::message& message) {
347 std::string objectName;
348 boost::container::flat_map<std::string, std::variant<std::string>>
349 values;
350 message.read(objectName, values);
351 auto findState = values.find(post::property);
352 if (findState != values.end())
353 {
354 biosHasPost =
355 std::get<std::string>(findState->second) != "Inactive";
356 }
357 });
James Feistfc94b212019-02-06 16:14:51 -0800358
James Feist8aeffd92020-09-14 12:08:28 -0700359 getPowerStatus(conn);
360 getPostStatus(conn);
James Feist3f0e8762018-11-27 11:30:42 -0800361}
James Feist87d713a2018-12-06 16:06:24 -0800362
363// replaces limits if MinReading and MaxReading are found.
364void findLimits(std::pair<double, double>& limits,
365 const SensorBaseConfiguration* data)
366{
367 if (!data)
368 {
369 return;
370 }
371 auto maxFind = data->second.find("MaxReading");
372 auto minFind = data->second.find("MinReading");
373
374 if (minFind != data->second.end())
375 {
James Feist3eb82622019-02-08 13:10:22 -0800376 limits.first = std::visit(VariantToDoubleVisitor(), minFind->second);
James Feist87d713a2018-12-06 16:06:24 -0800377 }
378 if (maxFind != data->second.end())
379 {
James Feist3eb82622019-02-08 13:10:22 -0800380 limits.second = std::visit(VariantToDoubleVisitor(), maxFind->second);
James Feist87d713a2018-12-06 16:06:24 -0800381 }
James Feistfc94b212019-02-06 16:14:51 -0800382}
James Feist82bac4c2019-03-11 11:16:53 -0700383
384void createAssociation(
385 std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
386 const std::string& path)
387{
388 if (association)
389 {
James Feistd2543f82019-04-09 11:10:06 -0700390 std::filesystem::path p(path);
391
James Feist82bac4c2019-03-11 11:16:53 -0700392 std::vector<Association> associations;
James Feistd2543f82019-04-09 11:10:06 -0700393 associations.push_back(
James Feistd8bd5622019-06-26 12:09:05 -0700394 Association("chassis", "all_sensors", p.parent_path().string()));
James Feist2adc95c2019-09-30 14:55:28 -0700395 association->register_property("Associations", associations);
James Feist82bac4c2019-03-11 11:16:53 -0700396 association->initialize();
397 }
James Feist43d32fe2019-09-04 10:35:20 -0700398}
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800399
400void setInventoryAssociation(
401 std::shared_ptr<sdbusplus::asio::dbus_interface> association,
AppaRao Pulic82213c2020-02-27 01:24:58 +0530402 const std::string& path,
403 const std::vector<std::string>& chassisPaths = std::vector<std::string>())
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800404{
405 if (association)
406 {
407 std::filesystem::path p(path);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800408 std::vector<Association> associations;
AppaRao Pulic82213c2020-02-27 01:24:58 +0530409 std::string objPath(p.parent_path().string());
410
411 associations.push_back(Association("inventory", "sensors", objPath));
412 associations.push_back(Association("chassis", "all_sensors", objPath));
413
414 for (const std::string& chassisPath : chassisPaths)
415 {
416 associations.push_back(
417 Association("chassis", "all_sensors", chassisPath));
418 }
419
James Feist2adc95c2019-09-30 14:55:28 -0700420 association->register_property("Associations", associations);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800421 association->initialize();
422 }
423}
424
425void createInventoryAssoc(
426 std::shared_ptr<sdbusplus::asio::connection> conn,
427 std::shared_ptr<sdbusplus::asio::dbus_interface> association,
428 const std::string& path)
429{
430 if (!association)
431 {
432 return;
433 }
AppaRao Pulic82213c2020-02-27 01:24:58 +0530434
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800435 conn->async_method_call(
436 [association, path](const boost::system::error_code ec,
AppaRao Pulic82213c2020-02-27 01:24:58 +0530437 const std::vector<std::string>& invSysObjPaths) {
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800438 if (ec)
439 {
AppaRao Pulic82213c2020-02-27 01:24:58 +0530440 // In case of error, set the default associations and
441 // initialize the association Interface.
442 setInventoryAssociation(association, path);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800443 return;
444 }
AppaRao Pulic82213c2020-02-27 01:24:58 +0530445 setInventoryAssociation(association, path, invSysObjPaths);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800446 },
447 mapper::busName, mapper::path, mapper::interface, "GetSubTreePaths",
448 "/xyz/openbmc_project/inventory/system", 2,
449 std::array<std::string, 1>{
450 "xyz.openbmc_project.Inventory.Item.System"});
451}
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200452
453std::optional<double> readFile(const std::string& thresholdFile,
454 const double& scaleFactor)
455{
456 std::string line;
457 std::ifstream labelFile(thresholdFile);
458 if (labelFile.good())
459 {
460 std::getline(labelFile, line);
461 labelFile.close();
462
463 try
464 {
465 return std::stod(line) / scaleFactor;
466 }
467 catch (const std::invalid_argument&)
468 {
469 return std::nullopt;
470 }
471 }
472 return std::nullopt;
473}
474
475std::optional<std::tuple<std::string, std::string, std::string>>
476 splitFileName(const std::filesystem::path& filePath)
477{
478 if (filePath.has_filename())
479 {
480 const auto fileName = filePath.filename().string();
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200481
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200482 size_t numberPos = std::strcspn(fileName.c_str(), "1234567890");
483 size_t itemPos = std::strcspn(fileName.c_str(), "_");
484
485 if (numberPos > 0 && itemPos > numberPos && fileName.size() > itemPos)
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200486 {
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200487 return std::make_optional(
488 std::make_tuple(fileName.substr(0, numberPos),
489 fileName.substr(numberPos, itemPos - numberPos),
490 fileName.substr(itemPos + 1, fileName.size())));
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200491 }
492 }
493 return std::nullopt;
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200494}