blob: ef70c1b3b5325d413545fe80aff4748e5b751ac4 [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
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103017#include "Utils.hpp"
18
Bruce Lee1263c3d2021-06-04 15:16:33 +080019#include "dbus-sensor_config.h"
20
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103021#include "DeviceMgmt.hpp"
22
Patrick Venture96e97db2019-10-31 13:44:38 -070023#include <boost/container/flat_map.hpp>
James Feist38fb5982020-05-28 10:09:54 -070024#include <sdbusplus/asio/connection.hpp>
25#include <sdbusplus/asio/object_server.hpp>
26#include <sdbusplus/bus/match.hpp>
27
James Feist24f02f22019-04-15 11:05:39 -070028#include <filesystem>
James Feist6714a252018-09-10 15:26:18 -070029#include <fstream>
Patrick Venture96e97db2019-10-31 13:44:38 -070030#include <memory>
James Feist6714a252018-09-10 15:26:18 -070031#include <regex>
Patrick Venture96e97db2019-10-31 13:44:38 -070032#include <stdexcept>
33#include <string>
34#include <utility>
35#include <variant>
36#include <vector>
James Feist6714a252018-09-10 15:26:18 -070037
James Feistcf3bce62019-01-08 10:07:19 -080038namespace fs = std::filesystem;
James Feist6714a252018-09-10 15:26:18 -070039
James Feist6ef20402019-01-07 16:45:08 -080040static bool powerStatusOn = false;
James Feistfc94b212019-02-06 16:14:51 -080041static bool biosHasPost = false;
Bruce Lee1263c3d2021-06-04 15:16:33 +080042static bool manufacturingMode = false;
Thu Nguyen6db8aae2022-10-04 08:12:48 +070043static bool chassisStatusOn = false;
James Feist58295ad2019-05-30 15:01:41 -070044
Patrick Williams92f8f512022-07-22 19:26:55 -050045static std::unique_ptr<sdbusplus::bus::match_t> powerMatch = nullptr;
46static std::unique_ptr<sdbusplus::bus::match_t> postMatch = nullptr;
Thu Nguyen6db8aae2022-10-04 08:12:48 +070047static std::unique_ptr<sdbusplus::bus::match_t> chassisMatch = nullptr;
James Feist71d31b22019-01-02 16:57:54 -080048
Jason Ling100c20b2020-08-11 14:50:33 -070049/**
50 * return the contents of a file
51 * @param[in] hwmonFile - the path to the file to read
52 * @return the contents of the file as a string or nullopt if the file could not
53 * be opened.
54 */
55
56std::optional<std::string> openAndRead(const std::string& hwmonFile)
57{
58 std::string fileVal;
59 std::ifstream fileStream(hwmonFile);
60 if (!fileStream.is_open())
61 {
62 return std::nullopt;
63 }
64 std::getline(fileStream, fileVal);
65 return fileVal;
66}
67
68/**
69 * given a hwmon temperature base name if valid return the full path else
70 * nullopt
71 * @param[in] directory - the hwmon sysfs directory
72 * @param[in] permitSet - a set of labels or hwmon basenames to permit. If this
73 * is empty then *everything* is permitted.
74 * @return a string to the full path of the file to create a temp sensor with or
75 * nullopt to indicate that no sensor should be created for this basename.
76 */
77std::optional<std::string>
78 getFullHwmonFilePath(const std::string& directory,
79 const std::string& hwmonBaseName,
80 const std::set<std::string>& permitSet)
81{
82 std::optional<std::string> result;
83 std::string filename;
84 if (permitSet.empty())
85 {
86 result = directory + "/" + hwmonBaseName + "_input";
87 return result;
88 }
89 filename = directory + "/" + hwmonBaseName + "_label";
90 auto searchVal = openAndRead(filename);
91 if (!searchVal)
92 {
93 /* if the hwmon temp doesn't have a corresponding label file
94 * then use the hwmon temperature base name
95 */
96 searchVal = hwmonBaseName;
97 }
98 if (permitSet.find(*searchVal) != permitSet.end())
99 {
100 result = directory + "/" + hwmonBaseName + "_input";
101 }
102 return result;
103}
104
105/**
106 * retrieve a set of basenames and labels to allow sensor creation for.
107 * @param[in] config - a map representing the configuration for a specific
108 * device
109 * @return a set of basenames and labels to allow sensor creation for. An empty
110 * set indicates that everything is permitted.
111 */
112std::set<std::string> getPermitSet(const SensorBaseConfigMap& config)
113{
114 auto permitAttribute = config.find("Labels");
115 std::set<std::string> permitSet;
116 if (permitAttribute != config.end())
117 {
118 try
119 {
120 auto val =
121 std::get<std::vector<std::string>>(permitAttribute->second);
122
123 permitSet.insert(std::make_move_iterator(val.begin()),
124 std::make_move_iterator(val.end()));
125 }
126 catch (const std::bad_variant_access& err)
127 {
128 std::cerr << err.what()
129 << ":PermitList does not contain a list, wrong "
130 "variant type.\n";
131 }
132 }
133 return permitSet;
134}
135
James Feist6714a252018-09-10 15:26:18 -0700136bool getSensorConfiguration(
137 const std::string& type,
138 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
Ed Tanous8a57ec02020-10-09 12:46:52 -0700139 ManagedObjectType& resp)
140{
141 return getSensorConfiguration(type, dbusConnection, resp, false);
142}
143
144bool getSensorConfiguration(
145 const std::string& type,
146 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
James Feist6714a252018-09-10 15:26:18 -0700147 ManagedObjectType& resp, bool useCache)
148{
149 static ManagedObjectType managedObj;
Zev Weiss054aad82022-08-18 01:37:34 -0700150 std::string typeIntf = configInterfaceName(type);
James Feist6714a252018-09-10 15:26:18 -0700151
152 if (!useCache)
153 {
154 managedObj.clear();
Patrick Williams92f8f512022-07-22 19:26:55 -0500155 sdbusplus::message_t getManagedObjects =
James Feist6714a252018-09-10 15:26:18 -0700156 dbusConnection->new_method_call(
JeffLin2c5a1f22022-10-05 15:19:09 +0800157 entityManagerName, "/xyz/openbmc_project/inventory",
158 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist6714a252018-09-10 15:26:18 -0700159 try
160 {
Patrick Williams92f8f512022-07-22 19:26:55 -0500161 sdbusplus::message_t reply =
James Feist6714a252018-09-10 15:26:18 -0700162 dbusConnection->call(getManagedObjects);
Yoo, Jae Hyun0e022052018-10-15 14:05:37 -0700163 reply.read(managedObj);
James Feist6714a252018-09-10 15:26:18 -0700164 }
Patrick Williams92f8f512022-07-22 19:26:55 -0500165 catch (const sdbusplus::exception_t& e)
James Feist6714a252018-09-10 15:26:18 -0700166 {
Jason Ling5747fab2019-10-02 16:46:23 -0700167 std::cerr << "While calling GetManagedObjects on service:"
168 << entityManagerName << " exception name:" << e.name()
169 << "and description:" << e.description()
170 << " was thrown\n";
James Feist6714a252018-09-10 15:26:18 -0700171 return false;
172 }
173 }
174 for (const auto& pathPair : managedObj)
175 {
Zev Weissfd3d5f72022-08-12 18:21:02 -0700176 for (const auto& [intf, cfg] : pathPair.second)
James Feist6714a252018-09-10 15:26:18 -0700177 {
Zev Weiss054aad82022-08-18 01:37:34 -0700178 if (intf.starts_with(typeIntf))
James Feist6714a252018-09-10 15:26:18 -0700179 {
Zev Weiss79d8aef2022-08-17 21:45:44 -0700180 resp.emplace(pathPair);
James Feist6714a252018-09-10 15:26:18 -0700181 break;
182 }
183 }
James Feist6714a252018-09-10 15:26:18 -0700184 }
185 return true;
186}
187
Lei YU6a4e9732021-10-20 13:27:34 +0800188bool findFiles(const fs::path& dirPath, std::string_view matchString,
Ed Tanous8a57ec02020-10-09 12:46:52 -0700189 std::vector<fs::path>& foundPaths, int symlinkDepth)
James Feist6714a252018-09-10 15:26:18 -0700190{
Lei YU6a4e9732021-10-20 13:27:34 +0800191 std::error_code ec;
192 if (!fs::exists(dirPath, ec))
Ed Tanous8a57ec02020-10-09 12:46:52 -0700193 {
James Feist6714a252018-09-10 15:26:18 -0700194 return false;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700195 }
James Feist6714a252018-09-10 15:26:18 -0700196
Lei YU6a4e9732021-10-20 13:27:34 +0800197 std::vector<std::regex> matchPieces;
198
199 size_t pos = 0;
200 std::string token;
201 // Generate the regex expressions list from the match we were given
202 while ((pos = matchString.find('/')) != std::string::npos)
203 {
204 token = matchString.substr(0, pos);
205 matchPieces.emplace_back(token);
206 matchString.remove_prefix(pos + 1);
207 }
208 matchPieces.emplace_back(std::string{matchString});
209
210 // Check if the match string contains directories, and skip the match of
211 // subdirectory if not
212 if (matchPieces.size() <= 1)
213 {
214 std::regex search(std::string{matchString});
215 std::smatch match;
216 for (auto p = fs::recursive_directory_iterator(
217 dirPath, fs::directory_options::follow_directory_symlink);
218 p != fs::recursive_directory_iterator(); ++p)
219 {
220 std::string path = p->path().string();
221 if (!is_directory(*p))
222 {
223 if (std::regex_search(path, match, search))
224 {
225 foundPaths.emplace_back(p->path());
226 }
227 }
228 if (p.depth() >= symlinkDepth)
229 {
230 p.disable_recursion_pending();
231 }
232 }
233 return true;
234 }
235
236 // The match string contains directories, verify each level of sub
237 // directories
Ed Tanous8a57ec02020-10-09 12:46:52 -0700238 for (auto p = fs::recursive_directory_iterator(
239 dirPath, fs::directory_options::follow_directory_symlink);
240 p != fs::recursive_directory_iterator(); ++p)
James Feist6714a252018-09-10 15:26:18 -0700241 {
Lei YU6a4e9732021-10-20 13:27:34 +0800242 std::vector<std::regex>::iterator matchPiece = matchPieces.begin();
243 fs::path::iterator pathIt = p->path().begin();
244 for (const fs::path& dir : dirPath)
245 {
246 if (dir.empty())
247 {
248 // When the path ends with '/', it gets am empty path
249 // skip such case.
250 break;
251 }
252 pathIt++;
253 }
254
255 while (pathIt != p->path().end())
256 {
257 // Found a path deeper than match.
258 if (matchPiece == matchPieces.end())
259 {
260 p.disable_recursion_pending();
261 break;
262 }
263 std::smatch match;
264 std::string component = pathIt->string();
265 std::regex regexPiece(*matchPiece);
266 if (!std::regex_match(component, match, regexPiece))
267 {
268 // path prefix doesn't match, no need to iterate further
269 p.disable_recursion_pending();
270 break;
271 }
272 matchPiece++;
273 pathIt++;
274 }
275
Ed Tanous8a57ec02020-10-09 12:46:52 -0700276 if (!is_directory(*p))
James Feist6714a252018-09-10 15:26:18 -0700277 {
Lei YU6a4e9732021-10-20 13:27:34 +0800278 if (matchPiece == matchPieces.end())
Ed Tanous8a57ec02020-10-09 12:46:52 -0700279 {
280 foundPaths.emplace_back(p->path());
281 }
James Feist6714a252018-09-10 15:26:18 -0700282 }
Lei YU6a4e9732021-10-20 13:27:34 +0800283
Ed Tanous8a57ec02020-10-09 12:46:52 -0700284 if (p.depth() >= symlinkDepth)
James Feist6714a252018-09-10 15:26:18 -0700285 {
Ed Tanous8a57ec02020-10-09 12:46:52 -0700286 p.disable_recursion_pending();
James Feist6714a252018-09-10 15:26:18 -0700287 }
288 }
289 return true;
290}
291
James Feist71d31b22019-01-02 16:57:54 -0800292bool isPowerOn(void)
James Feist6714a252018-09-10 15:26:18 -0700293{
James Feist71d31b22019-01-02 16:57:54 -0800294 if (!powerMatch)
James Feist6714a252018-09-10 15:26:18 -0700295 {
James Feist71d31b22019-01-02 16:57:54 -0800296 throw std::runtime_error("Power Match Not Created");
James Feist6714a252018-09-10 15:26:18 -0700297 }
James Feist71d31b22019-01-02 16:57:54 -0800298 return powerStatusOn;
299}
300
James Feistfc94b212019-02-06 16:14:51 -0800301bool hasBiosPost(void)
302{
James Feist52497fd2019-06-07 13:01:33 -0700303 if (!postMatch)
James Feistfc94b212019-02-06 16:14:51 -0800304 {
James Feist52497fd2019-06-07 13:01:33 -0700305 throw std::runtime_error("Post Match Not Created");
James Feistfc94b212019-02-06 16:14:51 -0800306 }
307 return biosHasPost;
308}
309
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700310bool isChassisOn(void)
311{
312 if (!chassisMatch)
313 {
314 throw std::runtime_error("Chassis On Match Not Created");
315 }
316 return chassisStatusOn;
317}
318
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000319bool readingStateGood(const PowerState& powerState)
320{
321 if (powerState == PowerState::on && !isPowerOn())
322 {
323 return false;
324 }
325 if (powerState == PowerState::biosPost && (!hasBiosPost() || !isPowerOn()))
326 {
327 return false;
328 }
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700329 if (powerState == PowerState::chassisOn && !isChassisOn())
330 {
331 return false;
332 }
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000333
334 return true;
335}
336
James Feist8aeffd92020-09-14 12:08:28 -0700337static void
338 getPowerStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
339 size_t retries = 2)
340{
341 conn->async_method_call(
342 [conn, retries](boost::system::error_code ec,
343 const std::variant<std::string>& state) {
Ed Tanousbb679322022-05-16 16:10:00 -0700344 if (ec)
345 {
Ed Tanous2049bd22022-07-09 07:20:26 -0700346 if (retries != 0U)
James Feist8aeffd92020-09-14 12:08:28 -0700347 {
Ed Tanousbb679322022-05-16 16:10:00 -0700348 auto timer = std::make_shared<boost::asio::steady_timer>(
349 conn->get_io_context());
350 timer->expires_after(std::chrono::seconds(15));
351 timer->async_wait(
352 [timer, conn, retries](boost::system::error_code) {
353 getPowerStatus(conn, retries - 1);
354 });
James Feist8aeffd92020-09-14 12:08:28 -0700355 return;
356 }
Ed Tanousbb679322022-05-16 16:10:00 -0700357
358 // we commonly come up before power control, we'll capture the
359 // property change later
360 std::cerr << "error getting power status " << ec.message() << "\n";
361 return;
362 }
Zev Weiss6c106d62022-08-17 20:50:00 -0700363 powerStatusOn = std::get<std::string>(state).ends_with(".Running");
James Feist8aeffd92020-09-14 12:08:28 -0700364 },
365 power::busname, power::path, properties::interface, properties::get,
366 power::interface, power::property);
367}
368
369static void
370 getPostStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
371 size_t retries = 2)
372{
373 conn->async_method_call(
374 [conn, retries](boost::system::error_code ec,
375 const std::variant<std::string>& state) {
Ed Tanousbb679322022-05-16 16:10:00 -0700376 if (ec)
377 {
Ed Tanous2049bd22022-07-09 07:20:26 -0700378 if (retries != 0U)
James Feist8aeffd92020-09-14 12:08:28 -0700379 {
Ed Tanousbb679322022-05-16 16:10:00 -0700380 auto timer = std::make_shared<boost::asio::steady_timer>(
381 conn->get_io_context());
382 timer->expires_after(std::chrono::seconds(15));
383 timer->async_wait(
384 [timer, conn, retries](boost::system::error_code) {
385 getPostStatus(conn, retries - 1);
386 });
James Feist8aeffd92020-09-14 12:08:28 -0700387 return;
388 }
Ed Tanousbb679322022-05-16 16:10:00 -0700389 // we commonly come up before power control, we'll capture the
390 // property change later
391 std::cerr << "error getting post status " << ec.message() << "\n";
392 return;
393 }
Ed Tanous2049bd22022-07-09 07:20:26 -0700394 const auto& value = std::get<std::string>(state);
Ed Tanousbb679322022-05-16 16:10:00 -0700395 biosHasPost = (value != "Inactive") &&
396 (value != "xyz.openbmc_project.State.OperatingSystem."
397 "Status.OSStatus.Inactive");
James Feist8aeffd92020-09-14 12:08:28 -0700398 },
399 post::busname, post::path, properties::interface, properties::get,
400 post::interface, post::property);
401}
402
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700403static void
404 getChassisStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
405 size_t retries = 2)
406{
407 conn->async_method_call(
408 [conn, retries](boost::system::error_code ec,
409 const std::variant<std::string>& state) {
410 if (ec)
411 {
412 if (retries != 0U)
413 {
414 auto timer = std::make_shared<boost::asio::steady_timer>(
415 conn->get_io_context());
416 timer->expires_after(std::chrono::seconds(15));
417 timer->async_wait(
418 [timer, conn, retries](boost::system::error_code) {
419 getChassisStatus(conn, retries - 1);
420 });
421 return;
422 }
423
424 // we commonly come up before power control, we'll capture the
425 // property change later
426 std::cerr << "error getting chassis power status " << ec.message()
427 << "\n";
428 return;
429 }
430 chassisStatusOn = std::get<std::string>(state).ends_with(chassis::sOn);
431 },
432 chassis::busname, chassis::path, properties::interface, properties::get,
433 chassis::interface, chassis::property);
434}
435
Zev Weiss88cb29d2022-05-09 03:46:15 +0000436void setupPowerMatchCallback(
437 const std::shared_ptr<sdbusplus::asio::connection>& conn,
438 std::function<void(PowerState type, bool state)>&& hostStatusCallback)
James Feist71d31b22019-01-02 16:57:54 -0800439{
James Feist43d32fe2019-09-04 10:35:20 -0700440 static boost::asio::steady_timer timer(conn->get_io_context());
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700441 static boost::asio::steady_timer timerChassisOn(conn->get_io_context());
James Feist6714a252018-09-10 15:26:18 -0700442 // create a match for powergood changes, first time do a method call to
James Feist71d31b22019-01-02 16:57:54 -0800443 // cache the correct value
James Feist52497fd2019-06-07 13:01:33 -0700444 if (powerMatch)
445 {
446 return;
447 }
James Feist6714a252018-09-10 15:26:18 -0700448
Patrick Williams92f8f512022-07-22 19:26:55 -0500449 powerMatch = std::make_unique<sdbusplus::bus::match_t>(
450 static_cast<sdbusplus::bus_t&>(*conn),
James Feist52497fd2019-06-07 13:01:33 -0700451 "type='signal',interface='" + std::string(properties::interface) +
452 "',path='" + std::string(power::path) + "',arg0='" +
453 std::string(power::interface) + "'",
Zev Weiss88cb29d2022-05-09 03:46:15 +0000454 [hostStatusCallback](sdbusplus::message_t& message) {
Ed Tanousbb679322022-05-16 16:10:00 -0700455 std::string objectName;
456 boost::container::flat_map<std::string, std::variant<std::string>>
457 values;
458 message.read(objectName, values);
459 auto findState = values.find(power::property);
460 if (findState != values.end())
461 {
Zev Weiss6c106d62022-08-17 20:50:00 -0700462 bool on =
463 std::get<std::string>(findState->second).ends_with(".Running");
Ed Tanousbb679322022-05-16 16:10:00 -0700464 if (!on)
James Feist6714a252018-09-10 15:26:18 -0700465 {
Ed Tanousbb679322022-05-16 16:10:00 -0700466 timer.cancel();
467 powerStatusOn = false;
Zev Weiss88cb29d2022-05-09 03:46:15 +0000468 hostStatusCallback(PowerState::on, powerStatusOn);
Ed Tanousbb679322022-05-16 16:10:00 -0700469 return;
470 }
471 // on comes too quickly
472 timer.expires_after(std::chrono::seconds(10));
Zev Weiss88cb29d2022-05-09 03:46:15 +0000473 timer.async_wait(
474 [hostStatusCallback](boost::system::error_code ec) {
Ed Tanousbb679322022-05-16 16:10:00 -0700475 if (ec == boost::asio::error::operation_aborted)
James Feist43d32fe2019-09-04 10:35:20 -0700476 {
James Feist43d32fe2019-09-04 10:35:20 -0700477 return;
478 }
Ed Tanousbb679322022-05-16 16:10:00 -0700479 if (ec)
480 {
481 std::cerr << "Timer error " << ec.message() << "\n";
482 return;
483 }
484 powerStatusOn = true;
Zev Weiss88cb29d2022-05-09 03:46:15 +0000485 hostStatusCallback(PowerState::on, powerStatusOn);
Ed Tanousbb679322022-05-16 16:10:00 -0700486 });
487 }
James Feist52497fd2019-06-07 13:01:33 -0700488 });
489
Patrick Williams92f8f512022-07-22 19:26:55 -0500490 postMatch = std::make_unique<sdbusplus::bus::match_t>(
491 static_cast<sdbusplus::bus_t&>(*conn),
James Feist52497fd2019-06-07 13:01:33 -0700492 "type='signal',interface='" + std::string(properties::interface) +
493 "',path='" + std::string(post::path) + "',arg0='" +
494 std::string(post::interface) + "'",
Zev Weiss88cb29d2022-05-09 03:46:15 +0000495 [hostStatusCallback](sdbusplus::message_t& message) {
Ed Tanousbb679322022-05-16 16:10:00 -0700496 std::string objectName;
497 boost::container::flat_map<std::string, std::variant<std::string>>
498 values;
499 message.read(objectName, values);
500 auto findState = values.find(post::property);
501 if (findState != values.end())
502 {
503 auto& value = std::get<std::string>(findState->second);
504 biosHasPost = (value != "Inactive") &&
505 (value != "xyz.openbmc_project.State.OperatingSystem."
506 "Status.OSStatus.Inactive");
Zev Weiss88cb29d2022-05-09 03:46:15 +0000507 hostStatusCallback(PowerState::biosPost, biosHasPost);
Ed Tanousbb679322022-05-16 16:10:00 -0700508 }
James Feist52497fd2019-06-07 13:01:33 -0700509 });
James Feistfc94b212019-02-06 16:14:51 -0800510
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700511 chassisMatch = std::make_unique<sdbusplus::bus::match_t>(
512 static_cast<sdbusplus::bus_t&>(*conn),
513 "type='signal',interface='" + std::string(properties::interface) +
514 "',path='" + std::string(chassis::path) + "',arg0='" +
515 std::string(chassis::interface) + "'",
516 [hostStatusCallback](sdbusplus::message_t& message) {
517 std::string objectName;
518 boost::container::flat_map<std::string, std::variant<std::string>>
519 values;
520 message.read(objectName, values);
521 auto findState = values.find(chassis::property);
522 if (findState != values.end())
523 {
524 bool on = std::get<std::string>(findState->second)
525 .ends_with(chassis::sOn);
526 if (!on)
527 {
528 timerChassisOn.cancel();
529 chassisStatusOn = false;
530 hostStatusCallback(PowerState::chassisOn, chassisStatusOn);
531 return;
532 }
533 // on comes too quickly
534 timerChassisOn.expires_after(std::chrono::seconds(10));
535 timerChassisOn.async_wait(
536 [hostStatusCallback](boost::system::error_code ec) {
537 if (ec == boost::asio::error::operation_aborted)
538 {
539 return;
540 }
541 if (ec)
542 {
543 std::cerr << "Timer error " << ec.message() << "\n";
544 return;
545 }
546 chassisStatusOn = true;
547 hostStatusCallback(PowerState::chassisOn, chassisStatusOn);
548 });
549 }
550 });
James Feist8aeffd92020-09-14 12:08:28 -0700551 getPowerStatus(conn);
552 getPostStatus(conn);
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700553 getChassisStatus(conn);
James Feist3f0e8762018-11-27 11:30:42 -0800554}
James Feist87d713a2018-12-06 16:06:24 -0800555
Zev Weiss88cb29d2022-05-09 03:46:15 +0000556void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
557{
558 setupPowerMatchCallback(conn, [](PowerState, bool) {});
559}
560
James Feist87d713a2018-12-06 16:06:24 -0800561// replaces limits if MinReading and MaxReading are found.
562void findLimits(std::pair<double, double>& limits,
563 const SensorBaseConfiguration* data)
564{
Ed Tanous2049bd22022-07-09 07:20:26 -0700565 if (data == nullptr)
James Feist87d713a2018-12-06 16:06:24 -0800566 {
567 return;
568 }
569 auto maxFind = data->second.find("MaxReading");
570 auto minFind = data->second.find("MinReading");
571
572 if (minFind != data->second.end())
573 {
James Feist3eb82622019-02-08 13:10:22 -0800574 limits.first = std::visit(VariantToDoubleVisitor(), minFind->second);
James Feist87d713a2018-12-06 16:06:24 -0800575 }
576 if (maxFind != data->second.end())
577 {
James Feist3eb82622019-02-08 13:10:22 -0800578 limits.second = std::visit(VariantToDoubleVisitor(), maxFind->second);
James Feist87d713a2018-12-06 16:06:24 -0800579 }
James Feistfc94b212019-02-06 16:14:51 -0800580}
James Feist82bac4c2019-03-11 11:16:53 -0700581
582void createAssociation(
583 std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
584 const std::string& path)
585{
586 if (association)
587 {
Lei YU6a4e9732021-10-20 13:27:34 +0800588 fs::path p(path);
James Feistd2543f82019-04-09 11:10:06 -0700589
James Feist82bac4c2019-03-11 11:16:53 -0700590 std::vector<Association> associations;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700591 associations.emplace_back("chassis", "all_sensors",
592 p.parent_path().string());
James Feist2adc95c2019-09-30 14:55:28 -0700593 association->register_property("Associations", associations);
James Feist82bac4c2019-03-11 11:16:53 -0700594 association->initialize();
595 }
James Feist43d32fe2019-09-04 10:35:20 -0700596}
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800597
598void setInventoryAssociation(
Ed Tanous8a57ec02020-10-09 12:46:52 -0700599 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000600 const std::string& inventoryPath, const std::string& chassisPath)
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800601{
602 if (association)
603 {
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800604 std::vector<Association> associations;
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000605 associations.emplace_back("inventory", "sensors", inventoryPath);
606 associations.emplace_back("chassis", "all_sensors", chassisPath);
AppaRao Pulic82213c2020-02-27 01:24:58 +0530607
James Feist2adc95c2019-09-30 14:55:28 -0700608 association->register_property("Associations", associations);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800609 association->initialize();
610 }
611}
612
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000613std::optional<std::string> findContainingChassis(std::string_view configParent,
614 const GetSubTreeType& subtree)
615{
616 // A parent that is a chassis takes precedence
617 for (const auto& [obj, services] : subtree)
618 {
619 if (obj == configParent)
620 {
621 return obj;
622 }
623 }
624
625 // If the parent is not a chassis, the system chassis is used. This does not
626 // work if there is more than one System, but we assume there is only one
627 // today.
628 for (const auto& [obj, services] : subtree)
629 {
630 for (const auto& [service, interfaces] : services)
631 {
632 if (std::find(interfaces.begin(), interfaces.end(),
633 "xyz.openbmc_project.Inventory.Item.System") !=
634 interfaces.end())
635 {
636 return obj;
637 }
638 }
639 }
640 return std::nullopt;
641}
642
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800643void createInventoryAssoc(
Ed Tanous8a57ec02020-10-09 12:46:52 -0700644 const std::shared_ptr<sdbusplus::asio::connection>& conn,
645 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800646 const std::string& path)
647{
648 if (!association)
649 {
650 return;
651 }
AppaRao Pulic82213c2020-02-27 01:24:58 +0530652
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000653 constexpr auto allInterfaces = std::to_array({
654 "xyz.openbmc_project.Inventory.Item.Board",
655 "xyz.openbmc_project.Inventory.Item.Chassis",
656 });
657
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800658 conn->async_method_call(
659 [association, path](const boost::system::error_code ec,
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000660 const GetSubTreeType& subtree) {
661 // The parent of the config is always the inventory object, and may be
662 // the associated chassis. If the parent is not itself a chassis or
663 // board, the sensor is associated with the system chassis.
664 std::string parent = fs::path(path).parent_path().string();
Ed Tanousbb679322022-05-16 16:10:00 -0700665 if (ec)
666 {
667 // In case of error, set the default associations and
668 // initialize the association Interface.
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000669 setInventoryAssociation(association, parent, parent);
Ed Tanousbb679322022-05-16 16:10:00 -0700670 return;
671 }
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000672 setInventoryAssociation(
673 association, parent,
674 findContainingChassis(parent, subtree).value_or(parent));
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800675 },
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000676 mapper::busName, mapper::path, mapper::interface, "GetSubTree",
677 "/xyz/openbmc_project/inventory/system", 2, allInterfaces);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800678}
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200679
680std::optional<double> readFile(const std::string& thresholdFile,
681 const double& scaleFactor)
682{
683 std::string line;
684 std::ifstream labelFile(thresholdFile);
685 if (labelFile.good())
686 {
687 std::getline(labelFile, line);
688 labelFile.close();
689
690 try
691 {
692 return std::stod(line) / scaleFactor;
693 }
694 catch (const std::invalid_argument&)
695 {
696 return std::nullopt;
697 }
698 }
699 return std::nullopt;
700}
701
702std::optional<std::tuple<std::string, std::string, std::string>>
Lei YU6a4e9732021-10-20 13:27:34 +0800703 splitFileName(const fs::path& filePath)
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200704{
705 if (filePath.has_filename())
706 {
707 const auto fileName = filePath.filename().string();
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200708
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200709 size_t numberPos = std::strcspn(fileName.c_str(), "1234567890");
710 size_t itemPos = std::strcspn(fileName.c_str(), "_");
711
712 if (numberPos > 0 && itemPos > numberPos && fileName.size() > itemPos)
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200713 {
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200714 return std::make_optional(
715 std::make_tuple(fileName.substr(0, numberPos),
716 fileName.substr(numberPos, itemPos - numberPos),
717 fileName.substr(itemPos + 1, fileName.size())));
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200718 }
719 }
720 return std::nullopt;
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200721}
Bruce Lee1263c3d2021-06-04 15:16:33 +0800722
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530723static void handleSpecialModeChange(const std::string& manufacturingModeStatus)
724{
725 manufacturingMode = false;
726 if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security."
727 "SpecialMode.Modes.Manufacturing")
728 {
729 manufacturingMode = true;
730 }
Ed Tanous74cffa82022-01-25 13:00:28 -0800731 if (validateUnsecureFeature == 1)
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530732 {
733 if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security."
734 "SpecialMode.Modes.ValidationUnsecure")
735 {
736 manufacturingMode = true;
737 }
738 }
739}
740
Bruce Lee1263c3d2021-06-04 15:16:33 +0800741void setupManufacturingModeMatch(sdbusplus::asio::connection& conn)
742{
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530743 namespace rules = sdbusplus::bus::match::rules;
744 static constexpr const char* specialModeInterface =
745 "xyz.openbmc_project.Security.SpecialMode";
746
747 const std::string filterSpecialModeIntfAdd =
748 rules::interfacesAdded() +
749 rules::argNpath(0, "/xyz/openbmc_project/security/special_mode");
Patrick Williams92f8f512022-07-22 19:26:55 -0500750 static std::unique_ptr<sdbusplus::bus::match_t> specialModeIntfMatch =
751 std::make_unique<sdbusplus::bus::match_t>(conn,
752 filterSpecialModeIntfAdd,
753 [](sdbusplus::message_t& m) {
Ed Tanousbb679322022-05-16 16:10:00 -0700754 sdbusplus::message::object_path path;
755 using PropertyMap =
756 boost::container::flat_map<std::string, std::variant<std::string>>;
757 boost::container::flat_map<std::string, PropertyMap> interfaceAdded;
758 m.read(path, interfaceAdded);
759 auto intfItr = interfaceAdded.find(specialModeInterface);
760 if (intfItr == interfaceAdded.end())
761 {
762 return;
763 }
764 PropertyMap& propertyList = intfItr->second;
765 auto itr = propertyList.find("SpecialMode");
766 if (itr == propertyList.end())
767 {
768 std::cerr << "error getting SpecialMode property "
769 << "\n";
770 return;
771 }
Ed Tanous2049bd22022-07-09 07:20:26 -0700772 auto* manufacturingModeStatus = std::get_if<std::string>(&itr->second);
Ed Tanousbb679322022-05-16 16:10:00 -0700773 handleSpecialModeChange(*manufacturingModeStatus);
Patrick Williams92f8f512022-07-22 19:26:55 -0500774 });
Bruce Lee1263c3d2021-06-04 15:16:33 +0800775
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530776 const std::string filterSpecialModeChange =
777 rules::type::signal() + rules::member("PropertiesChanged") +
778 rules::interface("org.freedesktop.DBus.Properties") +
779 rules::argN(0, specialModeInterface);
Patrick Williams92f8f512022-07-22 19:26:55 -0500780 static std::unique_ptr<sdbusplus::bus::match_t> specialModeChangeMatch =
781 std::make_unique<sdbusplus::bus::match_t>(conn, filterSpecialModeChange,
782 [](sdbusplus::message_t& m) {
Ed Tanousbb679322022-05-16 16:10:00 -0700783 std::string interfaceName;
784 boost::container::flat_map<std::string, std::variant<std::string>>
785 propertiesChanged;
Bruce Lee1263c3d2021-06-04 15:16:33 +0800786
Ed Tanousbb679322022-05-16 16:10:00 -0700787 m.read(interfaceName, propertiesChanged);
788 auto itr = propertiesChanged.find("SpecialMode");
789 if (itr == propertiesChanged.end())
790 {
791 return;
792 }
Ed Tanous2049bd22022-07-09 07:20:26 -0700793 auto* manufacturingModeStatus = std::get_if<std::string>(&itr->second);
Ed Tanousbb679322022-05-16 16:10:00 -0700794 handleSpecialModeChange(*manufacturingModeStatus);
Patrick Williams92f8f512022-07-22 19:26:55 -0500795 });
Bruce Lee1263c3d2021-06-04 15:16:33 +0800796
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530797 conn.async_method_call(
798 [](const boost::system::error_code ec,
799 const std::variant<std::string>& getManufactMode) {
Ed Tanousbb679322022-05-16 16:10:00 -0700800 if (ec)
801 {
802 std::cerr << "error getting SpecialMode status " << ec.message()
803 << "\n";
804 return;
805 }
Ed Tanous2049bd22022-07-09 07:20:26 -0700806 const auto* manufacturingModeStatus =
Ed Tanousbb679322022-05-16 16:10:00 -0700807 std::get_if<std::string>(&getManufactMode);
808 handleSpecialModeChange(*manufacturingModeStatus);
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530809 },
810 "xyz.openbmc_project.SpecialMode",
811 "/xyz/openbmc_project/security/special_mode",
812 "org.freedesktop.DBus.Properties", "Get", specialModeInterface,
813 "SpecialMode");
Bruce Lee1263c3d2021-06-04 15:16:33 +0800814}
815
816bool getManufacturingMode()
817{
818 return manufacturingMode;
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000819}
Zev Weiss214d9712022-08-12 12:54:31 -0700820
821std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
822 setupPropertiesChangedMatches(
823 sdbusplus::asio::connection& bus, std::span<const char* const> types,
824 const std::function<void(sdbusplus::message_t&)>& handler)
825{
826 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches;
827 for (const char* type : types)
828 {
829 auto match = std::make_unique<sdbusplus::bus::match_t>(
830 static_cast<sdbusplus::bus_t&>(bus),
831 "type='signal',member='PropertiesChanged',path_namespace='" +
Zev Weiss054aad82022-08-18 01:37:34 -0700832 std::string(inventoryPath) + "',arg0namespace='" +
833 configInterfaceName(type) + "'",
Zev Weiss214d9712022-08-12 12:54:31 -0700834 handler);
835 matches.emplace_back(std::move(match));
836 }
837 return matches;
838}
Zev Weissdabd48d2022-08-03 15:43:17 -0700839
840std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
841 setupPropertiesChangedMatches(
842 sdbusplus::asio::connection& bus, const I2CDeviceTypeMap& typeMap,
843 const std::function<void(sdbusplus::message_t&)>& handler)
844{
845 std::vector<const char*> types;
846 types.reserve(typeMap.size());
847 for (const auto& [type, dt] : typeMap)
848 {
849 types.push_back(type.data());
850 }
851 return setupPropertiesChangedMatches(bus, {types}, handler);
852}
Akshit Shah03d333e2023-08-23 22:14:28 +0000853
854bool getDeviceBusAddr(const std::string& deviceName, size_t& bus, size_t& addr)
855{
856 auto findHyphen = deviceName.find('-');
857 if (findHyphen == std::string::npos)
858 {
859 std::cerr << "found bad device " << deviceName << "\n";
860 return false;
861 }
862 std::string busStr = deviceName.substr(0, findHyphen);
863 std::string addrStr = deviceName.substr(findHyphen + 1);
864
865 std::from_chars_result res{};
866 res = std::from_chars(&*busStr.begin(), &*busStr.end(), bus);
867 if (res.ec != std::errc{} || res.ptr != &*busStr.end())
868 {
869 std::cerr << "Error finding bus for " << deviceName << "\n";
870 return false;
871 }
872 res = std::from_chars(&*addrStr.begin(), &*addrStr.end(), addr, 16);
873 if (res.ec != std::errc{} || res.ptr != &*addrStr.end())
874 {
875 std::cerr << "Error finding addr for " << deviceName << "\n";
876 return false;
877 }
878
879 return true;
880}