blob: 52171290d11f1f18c8f082c1470bba8d85bdf5e5 [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
Ed Tanous8a57ec02020-10-09 12:46:52 -070017#include <Utils.hpp>
James Feist6714a252018-09-10 15:26:18 -070018#include <boost/algorithm/string/predicate.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070019#include <boost/container/flat_map.hpp>
James Feist38fb5982020-05-28 10:09:54 -070020#include <sdbusplus/asio/connection.hpp>
21#include <sdbusplus/asio/object_server.hpp>
22#include <sdbusplus/bus/match.hpp>
23
James Feist24f02f22019-04-15 11:05:39 -070024#include <filesystem>
James Feist6714a252018-09-10 15:26:18 -070025#include <fstream>
Patrick Venture96e97db2019-10-31 13:44:38 -070026#include <memory>
James Feist6714a252018-09-10 15:26:18 -070027#include <regex>
Patrick Venture96e97db2019-10-31 13:44:38 -070028#include <stdexcept>
29#include <string>
30#include <utility>
31#include <variant>
32#include <vector>
James Feist6714a252018-09-10 15:26:18 -070033
James Feistcf3bce62019-01-08 10:07:19 -080034namespace fs = std::filesystem;
James Feist6714a252018-09-10 15:26:18 -070035
James Feist6ef20402019-01-07 16:45:08 -080036static bool powerStatusOn = false;
James Feistfc94b212019-02-06 16:14:51 -080037static bool biosHasPost = false;
James Feist58295ad2019-05-30 15:01:41 -070038
James Feist6ef20402019-01-07 16:45:08 -080039static std::unique_ptr<sdbusplus::bus::match::match> powerMatch = nullptr;
James Feist52497fd2019-06-07 13:01:33 -070040static std::unique_ptr<sdbusplus::bus::match::match> postMatch = nullptr;
James Feist71d31b22019-01-02 16:57:54 -080041
Jason Ling100c20b2020-08-11 14:50:33 -070042/**
43 * return the contents of a file
44 * @param[in] hwmonFile - the path to the file to read
45 * @return the contents of the file as a string or nullopt if the file could not
46 * be opened.
47 */
48
49std::optional<std::string> openAndRead(const std::string& hwmonFile)
50{
51 std::string fileVal;
52 std::ifstream fileStream(hwmonFile);
53 if (!fileStream.is_open())
54 {
55 return std::nullopt;
56 }
57 std::getline(fileStream, fileVal);
58 return fileVal;
59}
60
61/**
62 * given a hwmon temperature base name if valid return the full path else
63 * nullopt
64 * @param[in] directory - the hwmon sysfs directory
65 * @param[in] permitSet - a set of labels or hwmon basenames to permit. If this
66 * is empty then *everything* is permitted.
67 * @return a string to the full path of the file to create a temp sensor with or
68 * nullopt to indicate that no sensor should be created for this basename.
69 */
70std::optional<std::string>
71 getFullHwmonFilePath(const std::string& directory,
72 const std::string& hwmonBaseName,
73 const std::set<std::string>& permitSet)
74{
75 std::optional<std::string> result;
76 std::string filename;
77 if (permitSet.empty())
78 {
79 result = directory + "/" + hwmonBaseName + "_input";
80 return result;
81 }
82 filename = directory + "/" + hwmonBaseName + "_label";
83 auto searchVal = openAndRead(filename);
84 if (!searchVal)
85 {
86 /* if the hwmon temp doesn't have a corresponding label file
87 * then use the hwmon temperature base name
88 */
89 searchVal = hwmonBaseName;
90 }
91 if (permitSet.find(*searchVal) != permitSet.end())
92 {
93 result = directory + "/" + hwmonBaseName + "_input";
94 }
95 return result;
96}
97
98/**
99 * retrieve a set of basenames and labels to allow sensor creation for.
100 * @param[in] config - a map representing the configuration for a specific
101 * device
102 * @return a set of basenames and labels to allow sensor creation for. An empty
103 * set indicates that everything is permitted.
104 */
105std::set<std::string> getPermitSet(const SensorBaseConfigMap& config)
106{
107 auto permitAttribute = config.find("Labels");
108 std::set<std::string> permitSet;
109 if (permitAttribute != config.end())
110 {
111 try
112 {
113 auto val =
114 std::get<std::vector<std::string>>(permitAttribute->second);
115
116 permitSet.insert(std::make_move_iterator(val.begin()),
117 std::make_move_iterator(val.end()));
118 }
119 catch (const std::bad_variant_access& err)
120 {
121 std::cerr << err.what()
122 << ":PermitList does not contain a list, wrong "
123 "variant type.\n";
124 }
125 }
126 return permitSet;
127}
128
James Feist6714a252018-09-10 15:26:18 -0700129bool getSensorConfiguration(
130 const std::string& type,
131 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
Ed Tanous8a57ec02020-10-09 12:46:52 -0700132 ManagedObjectType& resp)
133{
134 return getSensorConfiguration(type, dbusConnection, resp, false);
135}
136
137bool getSensorConfiguration(
138 const std::string& type,
139 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
James Feist6714a252018-09-10 15:26:18 -0700140 ManagedObjectType& resp, bool useCache)
141{
142 static ManagedObjectType managedObj;
143
144 if (!useCache)
145 {
146 managedObj.clear();
147 sdbusplus::message::message getManagedObjects =
148 dbusConnection->new_method_call(
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700149 entityManagerName, "/", "org.freedesktop.DBus.ObjectManager",
James Feist6714a252018-09-10 15:26:18 -0700150 "GetManagedObjects");
151 bool err = false;
152 try
153 {
154 sdbusplus::message::message reply =
155 dbusConnection->call(getManagedObjects);
Yoo, Jae Hyun0e022052018-10-15 14:05:37 -0700156 reply.read(managedObj);
James Feist6714a252018-09-10 15:26:18 -0700157 }
Jason Ling5747fab2019-10-02 16:46:23 -0700158 catch (const sdbusplus::exception::exception& e)
James Feist6714a252018-09-10 15:26:18 -0700159 {
Jason Ling5747fab2019-10-02 16:46:23 -0700160 std::cerr << "While calling GetManagedObjects on service:"
161 << entityManagerName << " exception name:" << e.name()
162 << "and description:" << e.description()
163 << " was thrown\n";
James Feist6714a252018-09-10 15:26:18 -0700164 err = true;
165 }
166
167 if (err)
168 {
169 std::cerr << "Error communicating to entity manager\n";
170 return false;
171 }
172 }
173 for (const auto& pathPair : managedObj)
174 {
James Feist6714a252018-09-10 15:26:18 -0700175 bool correctType = false;
176 for (const auto& entry : pathPair.second)
177 {
178 if (boost::starts_with(entry.first, type))
179 {
180 correctType = true;
181 break;
182 }
183 }
184 if (correctType)
185 {
186 resp.emplace(pathPair);
187 }
188 }
189 return true;
190}
191
Ed Tanous8a57ec02020-10-09 12:46:52 -0700192bool findFiles(const fs::path& dirPath, const std::string& matchString,
193 std::vector<fs::path>& foundPaths, int symlinkDepth)
James Feist6714a252018-09-10 15:26:18 -0700194{
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700195 if (!fs::exists(dirPath))
Ed Tanous8a57ec02020-10-09 12:46:52 -0700196 {
James Feist6714a252018-09-10 15:26:18 -0700197 return false;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700198 }
James Feist6714a252018-09-10 15:26:18 -0700199
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700200 std::regex search(matchString);
James Feist6714a252018-09-10 15:26:18 -0700201 std::smatch match;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700202 for (auto p = fs::recursive_directory_iterator(
203 dirPath, fs::directory_options::follow_directory_symlink);
204 p != fs::recursive_directory_iterator(); ++p)
James Feist6714a252018-09-10 15:26:18 -0700205 {
Ed Tanous8a57ec02020-10-09 12:46:52 -0700206 std::string path = p->path().string();
207 if (!is_directory(*p))
James Feist6714a252018-09-10 15:26:18 -0700208 {
209 if (std::regex_search(path, match, search))
Ed Tanous8a57ec02020-10-09 12:46:52 -0700210 {
211 foundPaths.emplace_back(p->path());
212 }
James Feist6714a252018-09-10 15:26:18 -0700213 }
Ed Tanous8a57ec02020-10-09 12:46:52 -0700214 if (p.depth() >= symlinkDepth)
James Feist6714a252018-09-10 15:26:18 -0700215 {
Ed Tanous8a57ec02020-10-09 12:46:52 -0700216 p.disable_recursion_pending();
James Feist6714a252018-09-10 15:26:18 -0700217 }
218 }
219 return true;
220}
221
James Feist71d31b22019-01-02 16:57:54 -0800222bool isPowerOn(void)
James Feist6714a252018-09-10 15:26:18 -0700223{
James Feist71d31b22019-01-02 16:57:54 -0800224 if (!powerMatch)
James Feist6714a252018-09-10 15:26:18 -0700225 {
James Feist71d31b22019-01-02 16:57:54 -0800226 throw std::runtime_error("Power Match Not Created");
James Feist6714a252018-09-10 15:26:18 -0700227 }
James Feist71d31b22019-01-02 16:57:54 -0800228 return powerStatusOn;
229}
230
James Feistfc94b212019-02-06 16:14:51 -0800231bool hasBiosPost(void)
232{
James Feist52497fd2019-06-07 13:01:33 -0700233 if (!postMatch)
James Feistfc94b212019-02-06 16:14:51 -0800234 {
James Feist52497fd2019-06-07 13:01:33 -0700235 throw std::runtime_error("Post Match Not Created");
James Feistfc94b212019-02-06 16:14:51 -0800236 }
237 return biosHasPost;
238}
239
James Feist8aeffd92020-09-14 12:08:28 -0700240static void
241 getPowerStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
242 size_t retries = 2)
243{
244 conn->async_method_call(
245 [conn, retries](boost::system::error_code ec,
246 const std::variant<std::string>& state) {
247 if (ec)
248 {
249 if (retries)
250 {
251 auto timer = std::make_shared<boost::asio::steady_timer>(
252 conn->get_io_context());
253 timer->expires_after(std::chrono::seconds(15));
254 timer->async_wait(
255 [timer, conn, retries](boost::system::error_code) {
256 getPowerStatus(conn, retries - 1);
257 });
258 return;
259 }
260
261 // we commonly come up before power control, we'll capture the
262 // property change later
263 std::cerr << "error getting power status " << ec.message()
264 << "\n";
265 return;
266 }
267 powerStatusOn =
268 boost::ends_with(std::get<std::string>(state), "Running");
269 },
270 power::busname, power::path, properties::interface, properties::get,
271 power::interface, power::property);
272}
273
274static void
275 getPostStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
276 size_t retries = 2)
277{
278 conn->async_method_call(
279 [conn, retries](boost::system::error_code ec,
280 const std::variant<std::string>& state) {
281 if (ec)
282 {
283 if (retries)
284 {
285 auto timer = std::make_shared<boost::asio::steady_timer>(
286 conn->get_io_context());
287 timer->expires_after(std::chrono::seconds(15));
288 timer->async_wait(
289 [timer, conn, retries](boost::system::error_code) {
290 getPostStatus(conn, retries - 1);
291 });
292 return;
293 }
294 // we commonly come up before power control, we'll capture the
295 // property change later
296 std::cerr << "error getting post status " << ec.message()
297 << "\n";
298 return;
299 }
300 biosHasPost = std::get<std::string>(state) != "Inactive";
301 },
302 post::busname, post::path, properties::interface, properties::get,
303 post::interface, post::property);
304}
305
James Feist71d31b22019-01-02 16:57:54 -0800306void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
307{
James Feist43d32fe2019-09-04 10:35:20 -0700308 static boost::asio::steady_timer timer(conn->get_io_context());
James Feist6714a252018-09-10 15:26:18 -0700309 // create a match for powergood changes, first time do a method call to
James Feist71d31b22019-01-02 16:57:54 -0800310 // cache the correct value
James Feist52497fd2019-06-07 13:01:33 -0700311 if (powerMatch)
312 {
313 return;
314 }
James Feist6714a252018-09-10 15:26:18 -0700315
316 powerMatch = std::make_unique<sdbusplus::bus::match::match>(
317 static_cast<sdbusplus::bus::bus&>(*conn),
James Feist52497fd2019-06-07 13:01:33 -0700318 "type='signal',interface='" + std::string(properties::interface) +
319 "',path='" + std::string(power::path) + "',arg0='" +
320 std::string(power::interface) + "'",
321 [](sdbusplus::message::message& message) {
322 std::string objectName;
323 boost::container::flat_map<std::string, std::variant<std::string>>
324 values;
325 message.read(objectName, values);
326 auto findState = values.find(power::property);
327 if (findState != values.end())
James Feist6714a252018-09-10 15:26:18 -0700328 {
James Feist43d32fe2019-09-04 10:35:20 -0700329 bool on = boost::ends_with(
James Feist52497fd2019-06-07 13:01:33 -0700330 std::get<std::string>(findState->second), "Running");
James Feist43d32fe2019-09-04 10:35:20 -0700331 if (!on)
332 {
333 timer.cancel();
334 powerStatusOn = false;
335 return;
336 }
337 // on comes too quickly
338 timer.expires_after(std::chrono::seconds(10));
339 timer.async_wait([](boost::system::error_code ec) {
340 if (ec == boost::asio::error::operation_aborted)
341 {
342 return;
343 }
Ed Tanous8a57ec02020-10-09 12:46:52 -0700344 if (ec)
James Feist43d32fe2019-09-04 10:35:20 -0700345 {
346 std::cerr << "Timer error " << ec.message() << "\n";
347 return;
348 }
349 powerStatusOn = true;
350 });
James Feist6714a252018-09-10 15:26:18 -0700351 }
James Feist52497fd2019-06-07 13:01:33 -0700352 });
353
354 postMatch = std::make_unique<sdbusplus::bus::match::match>(
355 static_cast<sdbusplus::bus::bus&>(*conn),
356 "type='signal',interface='" + std::string(properties::interface) +
357 "',path='" + std::string(post::path) + "',arg0='" +
358 std::string(post::interface) + "'",
359 [](sdbusplus::message::message& message) {
360 std::string objectName;
361 boost::container::flat_map<std::string, std::variant<std::string>>
362 values;
363 message.read(objectName, values);
364 auto findState = values.find(post::property);
365 if (findState != values.end())
366 {
367 biosHasPost =
368 std::get<std::string>(findState->second) != "Inactive";
369 }
370 });
James Feistfc94b212019-02-06 16:14:51 -0800371
James Feist8aeffd92020-09-14 12:08:28 -0700372 getPowerStatus(conn);
373 getPostStatus(conn);
James Feist3f0e8762018-11-27 11:30:42 -0800374}
James Feist87d713a2018-12-06 16:06:24 -0800375
376// replaces limits if MinReading and MaxReading are found.
377void findLimits(std::pair<double, double>& limits,
378 const SensorBaseConfiguration* data)
379{
380 if (!data)
381 {
382 return;
383 }
384 auto maxFind = data->second.find("MaxReading");
385 auto minFind = data->second.find("MinReading");
386
387 if (minFind != data->second.end())
388 {
James Feist3eb82622019-02-08 13:10:22 -0800389 limits.first = std::visit(VariantToDoubleVisitor(), minFind->second);
James Feist87d713a2018-12-06 16:06:24 -0800390 }
391 if (maxFind != data->second.end())
392 {
James Feist3eb82622019-02-08 13:10:22 -0800393 limits.second = std::visit(VariantToDoubleVisitor(), maxFind->second);
James Feist87d713a2018-12-06 16:06:24 -0800394 }
James Feistfc94b212019-02-06 16:14:51 -0800395}
James Feist82bac4c2019-03-11 11:16:53 -0700396
397void createAssociation(
398 std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
399 const std::string& path)
400{
401 if (association)
402 {
James Feistd2543f82019-04-09 11:10:06 -0700403 std::filesystem::path p(path);
404
James Feist82bac4c2019-03-11 11:16:53 -0700405 std::vector<Association> associations;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700406 associations.emplace_back("chassis", "all_sensors",
407 p.parent_path().string());
James Feist2adc95c2019-09-30 14:55:28 -0700408 association->register_property("Associations", associations);
James Feist82bac4c2019-03-11 11:16:53 -0700409 association->initialize();
410 }
James Feist43d32fe2019-09-04 10:35:20 -0700411}
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800412
413void setInventoryAssociation(
Ed Tanous8a57ec02020-10-09 12:46:52 -0700414 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
AppaRao Pulic82213c2020-02-27 01:24:58 +0530415 const std::string& path,
416 const std::vector<std::string>& chassisPaths = std::vector<std::string>())
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800417{
418 if (association)
419 {
420 std::filesystem::path p(path);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800421 std::vector<Association> associations;
AppaRao Pulic82213c2020-02-27 01:24:58 +0530422 std::string objPath(p.parent_path().string());
423
Ed Tanous8a57ec02020-10-09 12:46:52 -0700424 associations.emplace_back("inventory", "sensors", objPath);
425 associations.emplace_back("chassis", "all_sensors", objPath);
AppaRao Pulic82213c2020-02-27 01:24:58 +0530426
427 for (const std::string& chassisPath : chassisPaths)
428 {
Ed Tanous8a57ec02020-10-09 12:46:52 -0700429 associations.emplace_back("chassis", "all_sensors", chassisPath);
AppaRao Pulic82213c2020-02-27 01:24:58 +0530430 }
431
James Feist2adc95c2019-09-30 14:55:28 -0700432 association->register_property("Associations", associations);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800433 association->initialize();
434 }
435}
436
437void createInventoryAssoc(
Ed Tanous8a57ec02020-10-09 12:46:52 -0700438 const std::shared_ptr<sdbusplus::asio::connection>& conn,
439 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800440 const std::string& path)
441{
442 if (!association)
443 {
444 return;
445 }
AppaRao Pulic82213c2020-02-27 01:24:58 +0530446
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800447 conn->async_method_call(
448 [association, path](const boost::system::error_code ec,
AppaRao Pulic82213c2020-02-27 01:24:58 +0530449 const std::vector<std::string>& invSysObjPaths) {
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800450 if (ec)
451 {
AppaRao Pulic82213c2020-02-27 01:24:58 +0530452 // In case of error, set the default associations and
453 // initialize the association Interface.
454 setInventoryAssociation(association, path);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800455 return;
456 }
AppaRao Pulic82213c2020-02-27 01:24:58 +0530457 setInventoryAssociation(association, path, invSysObjPaths);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800458 },
459 mapper::busName, mapper::path, mapper::interface, "GetSubTreePaths",
460 "/xyz/openbmc_project/inventory/system", 2,
461 std::array<std::string, 1>{
462 "xyz.openbmc_project.Inventory.Item.System"});
463}
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200464
465std::optional<double> readFile(const std::string& thresholdFile,
466 const double& scaleFactor)
467{
468 std::string line;
469 std::ifstream labelFile(thresholdFile);
470 if (labelFile.good())
471 {
472 std::getline(labelFile, line);
473 labelFile.close();
474
475 try
476 {
477 return std::stod(line) / scaleFactor;
478 }
479 catch (const std::invalid_argument&)
480 {
481 return std::nullopt;
482 }
483 }
484 return std::nullopt;
485}
486
487std::optional<std::tuple<std::string, std::string, std::string>>
488 splitFileName(const std::filesystem::path& filePath)
489{
490 if (filePath.has_filename())
491 {
492 const auto fileName = filePath.filename().string();
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200493
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200494 size_t numberPos = std::strcspn(fileName.c_str(), "1234567890");
495 size_t itemPos = std::strcspn(fileName.c_str(), "_");
496
497 if (numberPos > 0 && itemPos > numberPos && fileName.size() > itemPos)
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200498 {
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200499 return std::make_optional(
500 std::make_tuple(fileName.substr(0, numberPos),
501 fileName.substr(numberPos, itemPos - numberPos),
502 fileName.substr(itemPos + 1, fileName.size())));
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200503 }
504 }
505 return std::nullopt;
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200506}