blob: cf0ce9a0d5194d3296b950a9d0171468e5cc4c55 [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
Bruce Lee1263c3d2021-06-04 15:16:33 +080017#include "dbus-sensor_config.h"
18
Ed Tanous8a57ec02020-10-09 12:46:52 -070019#include <Utils.hpp>
James Feist6714a252018-09-10 15:26:18 -070020#include <boost/algorithm/string/predicate.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070021#include <boost/container/flat_map.hpp>
James Feist38fb5982020-05-28 10:09:54 -070022#include <sdbusplus/asio/connection.hpp>
23#include <sdbusplus/asio/object_server.hpp>
24#include <sdbusplus/bus/match.hpp>
25
James Feist24f02f22019-04-15 11:05:39 -070026#include <filesystem>
James Feist6714a252018-09-10 15:26:18 -070027#include <fstream>
Patrick Venture96e97db2019-10-31 13:44:38 -070028#include <memory>
James Feist6714a252018-09-10 15:26:18 -070029#include <regex>
Patrick Venture96e97db2019-10-31 13:44:38 -070030#include <stdexcept>
31#include <string>
32#include <utility>
33#include <variant>
34#include <vector>
James Feist6714a252018-09-10 15:26:18 -070035
James Feistcf3bce62019-01-08 10:07:19 -080036namespace fs = std::filesystem;
James Feist6714a252018-09-10 15:26:18 -070037
James Feist6ef20402019-01-07 16:45:08 -080038static bool powerStatusOn = false;
James Feistfc94b212019-02-06 16:14:51 -080039static bool biosHasPost = false;
Bruce Lee1263c3d2021-06-04 15:16:33 +080040static bool manufacturingMode = false;
James Feist58295ad2019-05-30 15:01:41 -070041
James Feist6ef20402019-01-07 16:45:08 -080042static std::unique_ptr<sdbusplus::bus::match::match> powerMatch = nullptr;
James Feist52497fd2019-06-07 13:01:33 -070043static std::unique_ptr<sdbusplus::bus::match::match> postMatch = nullptr;
James Feist71d31b22019-01-02 16:57:54 -080044
Jason Ling100c20b2020-08-11 14:50:33 -070045/**
46 * return the contents of a file
47 * @param[in] hwmonFile - the path to the file to read
48 * @return the contents of the file as a string or nullopt if the file could not
49 * be opened.
50 */
51
52std::optional<std::string> openAndRead(const std::string& hwmonFile)
53{
54 std::string fileVal;
55 std::ifstream fileStream(hwmonFile);
56 if (!fileStream.is_open())
57 {
58 return std::nullopt;
59 }
60 std::getline(fileStream, fileVal);
61 return fileVal;
62}
63
64/**
65 * given a hwmon temperature base name if valid return the full path else
66 * nullopt
67 * @param[in] directory - the hwmon sysfs directory
68 * @param[in] permitSet - a set of labels or hwmon basenames to permit. If this
69 * is empty then *everything* is permitted.
70 * @return a string to the full path of the file to create a temp sensor with or
71 * nullopt to indicate that no sensor should be created for this basename.
72 */
73std::optional<std::string>
74 getFullHwmonFilePath(const std::string& directory,
75 const std::string& hwmonBaseName,
76 const std::set<std::string>& permitSet)
77{
78 std::optional<std::string> result;
79 std::string filename;
80 if (permitSet.empty())
81 {
82 result = directory + "/" + hwmonBaseName + "_input";
83 return result;
84 }
85 filename = directory + "/" + hwmonBaseName + "_label";
86 auto searchVal = openAndRead(filename);
87 if (!searchVal)
88 {
89 /* if the hwmon temp doesn't have a corresponding label file
90 * then use the hwmon temperature base name
91 */
92 searchVal = hwmonBaseName;
93 }
94 if (permitSet.find(*searchVal) != permitSet.end())
95 {
96 result = directory + "/" + hwmonBaseName + "_input";
97 }
98 return result;
99}
100
101/**
102 * retrieve a set of basenames and labels to allow sensor creation for.
103 * @param[in] config - a map representing the configuration for a specific
104 * device
105 * @return a set of basenames and labels to allow sensor creation for. An empty
106 * set indicates that everything is permitted.
107 */
108std::set<std::string> getPermitSet(const SensorBaseConfigMap& config)
109{
110 auto permitAttribute = config.find("Labels");
111 std::set<std::string> permitSet;
112 if (permitAttribute != config.end())
113 {
114 try
115 {
116 auto val =
117 std::get<std::vector<std::string>>(permitAttribute->second);
118
119 permitSet.insert(std::make_move_iterator(val.begin()),
120 std::make_move_iterator(val.end()));
121 }
122 catch (const std::bad_variant_access& err)
123 {
124 std::cerr << err.what()
125 << ":PermitList does not contain a list, wrong "
126 "variant type.\n";
127 }
128 }
129 return permitSet;
130}
131
James Feist6714a252018-09-10 15:26:18 -0700132bool getSensorConfiguration(
133 const std::string& type,
134 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
Ed Tanous8a57ec02020-10-09 12:46:52 -0700135 ManagedObjectType& resp)
136{
137 return getSensorConfiguration(type, dbusConnection, resp, false);
138}
139
140bool getSensorConfiguration(
141 const std::string& type,
142 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
James Feist6714a252018-09-10 15:26:18 -0700143 ManagedObjectType& resp, bool useCache)
144{
145 static ManagedObjectType managedObj;
146
147 if (!useCache)
148 {
149 managedObj.clear();
150 sdbusplus::message::message getManagedObjects =
151 dbusConnection->new_method_call(
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700152 entityManagerName, "/", "org.freedesktop.DBus.ObjectManager",
James Feist6714a252018-09-10 15:26:18 -0700153 "GetManagedObjects");
154 bool err = false;
155 try
156 {
157 sdbusplus::message::message reply =
158 dbusConnection->call(getManagedObjects);
Yoo, Jae Hyun0e022052018-10-15 14:05:37 -0700159 reply.read(managedObj);
James Feist6714a252018-09-10 15:26:18 -0700160 }
Jason Ling5747fab2019-10-02 16:46:23 -0700161 catch (const sdbusplus::exception::exception& e)
James Feist6714a252018-09-10 15:26:18 -0700162 {
Jason Ling5747fab2019-10-02 16:46:23 -0700163 std::cerr << "While calling GetManagedObjects on service:"
164 << entityManagerName << " exception name:" << e.name()
165 << "and description:" << e.description()
166 << " was thrown\n";
James Feist6714a252018-09-10 15:26:18 -0700167 err = true;
168 }
169
170 if (err)
171 {
172 std::cerr << "Error communicating to entity manager\n";
173 return false;
174 }
175 }
176 for (const auto& pathPair : managedObj)
177 {
James Feist6714a252018-09-10 15:26:18 -0700178 bool correctType = false;
179 for (const auto& entry : pathPair.second)
180 {
181 if (boost::starts_with(entry.first, type))
182 {
183 correctType = true;
184 break;
185 }
186 }
187 if (correctType)
188 {
189 resp.emplace(pathPair);
190 }
191 }
192 return true;
193}
194
Ed Tanous8a57ec02020-10-09 12:46:52 -0700195bool findFiles(const fs::path& dirPath, const std::string& matchString,
196 std::vector<fs::path>& foundPaths, int symlinkDepth)
James Feist6714a252018-09-10 15:26:18 -0700197{
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700198 if (!fs::exists(dirPath))
Ed Tanous8a57ec02020-10-09 12:46:52 -0700199 {
James Feist6714a252018-09-10 15:26:18 -0700200 return false;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700201 }
James Feist6714a252018-09-10 15:26:18 -0700202
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700203 std::regex search(matchString);
James Feist6714a252018-09-10 15:26:18 -0700204 std::smatch match;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700205 for (auto p = fs::recursive_directory_iterator(
206 dirPath, fs::directory_options::follow_directory_symlink);
207 p != fs::recursive_directory_iterator(); ++p)
James Feist6714a252018-09-10 15:26:18 -0700208 {
Ed Tanous8a57ec02020-10-09 12:46:52 -0700209 std::string path = p->path().string();
210 if (!is_directory(*p))
James Feist6714a252018-09-10 15:26:18 -0700211 {
212 if (std::regex_search(path, match, search))
Ed Tanous8a57ec02020-10-09 12:46:52 -0700213 {
214 foundPaths.emplace_back(p->path());
215 }
James Feist6714a252018-09-10 15:26:18 -0700216 }
Ed Tanous8a57ec02020-10-09 12:46:52 -0700217 if (p.depth() >= symlinkDepth)
James Feist6714a252018-09-10 15:26:18 -0700218 {
Ed Tanous8a57ec02020-10-09 12:46:52 -0700219 p.disable_recursion_pending();
James Feist6714a252018-09-10 15:26:18 -0700220 }
221 }
222 return true;
223}
224
James Feist71d31b22019-01-02 16:57:54 -0800225bool isPowerOn(void)
James Feist6714a252018-09-10 15:26:18 -0700226{
James Feist71d31b22019-01-02 16:57:54 -0800227 if (!powerMatch)
James Feist6714a252018-09-10 15:26:18 -0700228 {
James Feist71d31b22019-01-02 16:57:54 -0800229 throw std::runtime_error("Power Match Not Created");
James Feist6714a252018-09-10 15:26:18 -0700230 }
James Feist71d31b22019-01-02 16:57:54 -0800231 return powerStatusOn;
232}
233
James Feistfc94b212019-02-06 16:14:51 -0800234bool hasBiosPost(void)
235{
James Feist52497fd2019-06-07 13:01:33 -0700236 if (!postMatch)
James Feistfc94b212019-02-06 16:14:51 -0800237 {
James Feist52497fd2019-06-07 13:01:33 -0700238 throw std::runtime_error("Post Match Not Created");
James Feistfc94b212019-02-06 16:14:51 -0800239 }
240 return biosHasPost;
241}
242
James Feist8aeffd92020-09-14 12:08:28 -0700243static void
244 getPowerStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
245 size_t retries = 2)
246{
247 conn->async_method_call(
248 [conn, retries](boost::system::error_code ec,
249 const std::variant<std::string>& state) {
250 if (ec)
251 {
252 if (retries)
253 {
254 auto timer = std::make_shared<boost::asio::steady_timer>(
255 conn->get_io_context());
256 timer->expires_after(std::chrono::seconds(15));
257 timer->async_wait(
258 [timer, conn, retries](boost::system::error_code) {
259 getPowerStatus(conn, retries - 1);
260 });
261 return;
262 }
263
264 // we commonly come up before power control, we'll capture the
265 // property change later
266 std::cerr << "error getting power status " << ec.message()
267 << "\n";
268 return;
269 }
270 powerStatusOn =
271 boost::ends_with(std::get<std::string>(state), "Running");
272 },
273 power::busname, power::path, properties::interface, properties::get,
274 power::interface, power::property);
275}
276
277static void
278 getPostStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
279 size_t retries = 2)
280{
281 conn->async_method_call(
282 [conn, retries](boost::system::error_code ec,
283 const std::variant<std::string>& state) {
284 if (ec)
285 {
286 if (retries)
287 {
288 auto timer = std::make_shared<boost::asio::steady_timer>(
289 conn->get_io_context());
290 timer->expires_after(std::chrono::seconds(15));
291 timer->async_wait(
292 [timer, conn, retries](boost::system::error_code) {
293 getPostStatus(conn, retries - 1);
294 });
295 return;
296 }
297 // we commonly come up before power control, we'll capture the
298 // property change later
299 std::cerr << "error getting post status " << ec.message()
300 << "\n";
301 return;
302 }
303 biosHasPost = std::get<std::string>(state) != "Inactive";
304 },
305 post::busname, post::path, properties::interface, properties::get,
306 post::interface, post::property);
307}
308
James Feist71d31b22019-01-02 16:57:54 -0800309void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
310{
James Feist43d32fe2019-09-04 10:35:20 -0700311 static boost::asio::steady_timer timer(conn->get_io_context());
James Feist6714a252018-09-10 15:26:18 -0700312 // create a match for powergood changes, first time do a method call to
James Feist71d31b22019-01-02 16:57:54 -0800313 // cache the correct value
James Feist52497fd2019-06-07 13:01:33 -0700314 if (powerMatch)
315 {
316 return;
317 }
James Feist6714a252018-09-10 15:26:18 -0700318
319 powerMatch = std::make_unique<sdbusplus::bus::match::match>(
320 static_cast<sdbusplus::bus::bus&>(*conn),
James Feist52497fd2019-06-07 13:01:33 -0700321 "type='signal',interface='" + std::string(properties::interface) +
322 "',path='" + std::string(power::path) + "',arg0='" +
323 std::string(power::interface) + "'",
324 [](sdbusplus::message::message& message) {
325 std::string objectName;
326 boost::container::flat_map<std::string, std::variant<std::string>>
327 values;
328 message.read(objectName, values);
329 auto findState = values.find(power::property);
330 if (findState != values.end())
James Feist6714a252018-09-10 15:26:18 -0700331 {
James Feist43d32fe2019-09-04 10:35:20 -0700332 bool on = boost::ends_with(
James Feist52497fd2019-06-07 13:01:33 -0700333 std::get<std::string>(findState->second), "Running");
James Feist43d32fe2019-09-04 10:35:20 -0700334 if (!on)
335 {
336 timer.cancel();
337 powerStatusOn = false;
338 return;
339 }
340 // on comes too quickly
341 timer.expires_after(std::chrono::seconds(10));
342 timer.async_wait([](boost::system::error_code ec) {
343 if (ec == boost::asio::error::operation_aborted)
344 {
345 return;
346 }
Ed Tanous8a57ec02020-10-09 12:46:52 -0700347 if (ec)
James Feist43d32fe2019-09-04 10:35:20 -0700348 {
349 std::cerr << "Timer error " << ec.message() << "\n";
350 return;
351 }
352 powerStatusOn = true;
353 });
James Feist6714a252018-09-10 15:26:18 -0700354 }
James Feist52497fd2019-06-07 13:01:33 -0700355 });
356
357 postMatch = std::make_unique<sdbusplus::bus::match::match>(
358 static_cast<sdbusplus::bus::bus&>(*conn),
359 "type='signal',interface='" + std::string(properties::interface) +
360 "',path='" + std::string(post::path) + "',arg0='" +
361 std::string(post::interface) + "'",
362 [](sdbusplus::message::message& message) {
363 std::string objectName;
364 boost::container::flat_map<std::string, std::variant<std::string>>
365 values;
366 message.read(objectName, values);
367 auto findState = values.find(post::property);
368 if (findState != values.end())
369 {
370 biosHasPost =
371 std::get<std::string>(findState->second) != "Inactive";
372 }
373 });
James Feistfc94b212019-02-06 16:14:51 -0800374
James Feist8aeffd92020-09-14 12:08:28 -0700375 getPowerStatus(conn);
376 getPostStatus(conn);
James Feist3f0e8762018-11-27 11:30:42 -0800377}
James Feist87d713a2018-12-06 16:06:24 -0800378
379// replaces limits if MinReading and MaxReading are found.
380void findLimits(std::pair<double, double>& limits,
381 const SensorBaseConfiguration* data)
382{
383 if (!data)
384 {
385 return;
386 }
387 auto maxFind = data->second.find("MaxReading");
388 auto minFind = data->second.find("MinReading");
389
390 if (minFind != data->second.end())
391 {
James Feist3eb82622019-02-08 13:10:22 -0800392 limits.first = std::visit(VariantToDoubleVisitor(), minFind->second);
James Feist87d713a2018-12-06 16:06:24 -0800393 }
394 if (maxFind != data->second.end())
395 {
James Feist3eb82622019-02-08 13:10:22 -0800396 limits.second = std::visit(VariantToDoubleVisitor(), maxFind->second);
James Feist87d713a2018-12-06 16:06:24 -0800397 }
James Feistfc94b212019-02-06 16:14:51 -0800398}
James Feist82bac4c2019-03-11 11:16:53 -0700399
400void createAssociation(
401 std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
402 const std::string& path)
403{
404 if (association)
405 {
James Feistd2543f82019-04-09 11:10:06 -0700406 std::filesystem::path p(path);
407
James Feist82bac4c2019-03-11 11:16:53 -0700408 std::vector<Association> associations;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700409 associations.emplace_back("chassis", "all_sensors",
410 p.parent_path().string());
James Feist2adc95c2019-09-30 14:55:28 -0700411 association->register_property("Associations", associations);
James Feist82bac4c2019-03-11 11:16:53 -0700412 association->initialize();
413 }
James Feist43d32fe2019-09-04 10:35:20 -0700414}
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800415
416void setInventoryAssociation(
Ed Tanous8a57ec02020-10-09 12:46:52 -0700417 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
AppaRao Pulic82213c2020-02-27 01:24:58 +0530418 const std::string& path,
419 const std::vector<std::string>& chassisPaths = std::vector<std::string>())
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800420{
421 if (association)
422 {
423 std::filesystem::path p(path);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800424 std::vector<Association> associations;
AppaRao Pulic82213c2020-02-27 01:24:58 +0530425 std::string objPath(p.parent_path().string());
426
Ed Tanous8a57ec02020-10-09 12:46:52 -0700427 associations.emplace_back("inventory", "sensors", objPath);
428 associations.emplace_back("chassis", "all_sensors", objPath);
AppaRao Pulic82213c2020-02-27 01:24:58 +0530429
430 for (const std::string& chassisPath : chassisPaths)
431 {
Ed Tanous8a57ec02020-10-09 12:46:52 -0700432 associations.emplace_back("chassis", "all_sensors", chassisPath);
AppaRao Pulic82213c2020-02-27 01:24:58 +0530433 }
434
James Feist2adc95c2019-09-30 14:55:28 -0700435 association->register_property("Associations", associations);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800436 association->initialize();
437 }
438}
439
440void createInventoryAssoc(
Ed Tanous8a57ec02020-10-09 12:46:52 -0700441 const std::shared_ptr<sdbusplus::asio::connection>& conn,
442 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800443 const std::string& path)
444{
445 if (!association)
446 {
447 return;
448 }
AppaRao Pulic82213c2020-02-27 01:24:58 +0530449
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800450 conn->async_method_call(
451 [association, path](const boost::system::error_code ec,
AppaRao Pulic82213c2020-02-27 01:24:58 +0530452 const std::vector<std::string>& invSysObjPaths) {
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800453 if (ec)
454 {
AppaRao Pulic82213c2020-02-27 01:24:58 +0530455 // In case of error, set the default associations and
456 // initialize the association Interface.
457 setInventoryAssociation(association, path);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800458 return;
459 }
AppaRao Pulic82213c2020-02-27 01:24:58 +0530460 setInventoryAssociation(association, path, invSysObjPaths);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800461 },
462 mapper::busName, mapper::path, mapper::interface, "GetSubTreePaths",
463 "/xyz/openbmc_project/inventory/system", 2,
464 std::array<std::string, 1>{
465 "xyz.openbmc_project.Inventory.Item.System"});
466}
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200467
468std::optional<double> readFile(const std::string& thresholdFile,
469 const double& scaleFactor)
470{
471 std::string line;
472 std::ifstream labelFile(thresholdFile);
473 if (labelFile.good())
474 {
475 std::getline(labelFile, line);
476 labelFile.close();
477
478 try
479 {
480 return std::stod(line) / scaleFactor;
481 }
482 catch (const std::invalid_argument&)
483 {
484 return std::nullopt;
485 }
486 }
487 return std::nullopt;
488}
489
490std::optional<std::tuple<std::string, std::string, std::string>>
491 splitFileName(const std::filesystem::path& filePath)
492{
493 if (filePath.has_filename())
494 {
495 const auto fileName = filePath.filename().string();
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200496
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200497 size_t numberPos = std::strcspn(fileName.c_str(), "1234567890");
498 size_t itemPos = std::strcspn(fileName.c_str(), "_");
499
500 if (numberPos > 0 && itemPos > numberPos && fileName.size() > itemPos)
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200501 {
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200502 return std::make_optional(
503 std::make_tuple(fileName.substr(0, numberPos),
504 fileName.substr(numberPos, itemPos - numberPos),
505 fileName.substr(itemPos + 1, fileName.size())));
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200506 }
507 }
508 return std::nullopt;
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200509}
Bruce Lee1263c3d2021-06-04 15:16:33 +0800510
511void setupManufacturingModeMatch(sdbusplus::asio::connection& conn)
512{
513 static std::unique_ptr<sdbusplus::bus::match::match>
514 setupManufacturingModeMatch =
515 std::make_unique<sdbusplus::bus::match::match>(
516 conn,
517 "type='signal',interface='org.freedesktop.DBus."
518 "Properties',member='"
519 "PropertiesChanged',arg0namespace='xyz.openbmc_project."
520 "Security.SpecialMode'",
521 [](sdbusplus::message::message& msg) {
522 std::string interfaceName;
523 boost::container::flat_map<std::string,
524 std::variant<std::string>>
525 propertiesChanged;
526 std::string manufacturingModeStatus;
527
528 msg.read(interfaceName, propertiesChanged);
529 if (propertiesChanged.begin() == propertiesChanged.end())
530 {
531 return;
532 }
533
534 manufacturingModeStatus = std::get<std::string>(
535 propertiesChanged.begin()->second);
536 manufacturingMode = false;
537 if (manufacturingModeStatus ==
538 "xyz.openbmc_project.Control.Security."
539 "SpecialMode.Modes.Manufacturing")
540 {
541 manufacturingMode = true;
542 }
543 if (validateUnsecureFeature == true)
544 {
545 if (manufacturingModeStatus ==
546 "xyz.openbmc_project.Control.Security."
547 "SpecialMode.Modes.ValidationUnsecure")
548 {
549 manufacturingMode = true;
550 }
551 }
552 });
553
554 return;
555}
556
557bool getManufacturingMode()
558{
559 return manufacturingMode;
560}