blob: 2af6b504317d73d8e4655daefc39e601e829fae5 [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"
Ed Tanouseacbfdd2024-04-04 12:00:24 -070022#include "VariantVisitors.hpp"
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103023
Ed Tanouseacbfdd2024-04-04 12:00:24 -070024#include <boost/asio/error.hpp>
25#include <boost/asio/steady_timer.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070026#include <boost/container/flat_map.hpp>
James Feist38fb5982020-05-28 10:09:54 -070027#include <sdbusplus/asio/connection.hpp>
28#include <sdbusplus/asio/object_server.hpp>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070029#include <sdbusplus/bus.hpp>
James Feist38fb5982020-05-28 10:09:54 -070030#include <sdbusplus/bus/match.hpp>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070031#include <sdbusplus/exception.hpp>
32#include <sdbusplus/message.hpp>
33#include <sdbusplus/message/native_types.hpp>
James Feist38fb5982020-05-28 10:09:54 -070034
Ed Tanouseacbfdd2024-04-04 12:00:24 -070035#include <algorithm>
36#include <array>
37#include <chrono>
38#include <cstddef>
39#include <cstring>
James Feist24f02f22019-04-15 11:05:39 -070040#include <filesystem>
James Feist6714a252018-09-10 15:26:18 -070041#include <fstream>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070042#include <functional>
43#include <iostream>
44#include <iterator>
Patrick Venture96e97db2019-10-31 13:44:38 -070045#include <memory>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070046#include <optional>
James Feist6714a252018-09-10 15:26:18 -070047#include <regex>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070048#include <set>
49#include <span>
Patrick Venture96e97db2019-10-31 13:44:38 -070050#include <stdexcept>
51#include <string>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070052#include <string_view>
53#include <system_error>
54#include <tuple>
Patrick Venture96e97db2019-10-31 13:44:38 -070055#include <utility>
56#include <variant>
57#include <vector>
James Feist6714a252018-09-10 15:26:18 -070058
James Feist6ef20402019-01-07 16:45:08 -080059static bool powerStatusOn = false;
James Feistfc94b212019-02-06 16:14:51 -080060static bool biosHasPost = false;
Bruce Lee1263c3d2021-06-04 15:16:33 +080061static bool manufacturingMode = false;
Thu Nguyen6db8aae2022-10-04 08:12:48 +070062static bool chassisStatusOn = false;
James Feist58295ad2019-05-30 15:01:41 -070063
Patrick Williams92f8f512022-07-22 19:26:55 -050064static std::unique_ptr<sdbusplus::bus::match_t> powerMatch = nullptr;
65static std::unique_ptr<sdbusplus::bus::match_t> postMatch = nullptr;
Thu Nguyen6db8aae2022-10-04 08:12:48 +070066static std::unique_ptr<sdbusplus::bus::match_t> chassisMatch = nullptr;
James Feist71d31b22019-01-02 16:57:54 -080067
Jason Ling100c20b2020-08-11 14:50:33 -070068/**
69 * return the contents of a file
70 * @param[in] hwmonFile - the path to the file to read
71 * @return the contents of the file as a string or nullopt if the file could not
72 * be opened.
73 */
74
75std::optional<std::string> openAndRead(const std::string& hwmonFile)
76{
77 std::string fileVal;
78 std::ifstream fileStream(hwmonFile);
79 if (!fileStream.is_open())
80 {
81 return std::nullopt;
82 }
83 std::getline(fileStream, fileVal);
84 return fileVal;
85}
86
87/**
88 * given a hwmon temperature base name if valid return the full path else
89 * nullopt
90 * @param[in] directory - the hwmon sysfs directory
91 * @param[in] permitSet - a set of labels or hwmon basenames to permit. If this
92 * is empty then *everything* is permitted.
93 * @return a string to the full path of the file to create a temp sensor with or
94 * nullopt to indicate that no sensor should be created for this basename.
95 */
Patrick Williams2aaf7172024-08-16 15:20:40 -040096std::optional<std::string> getFullHwmonFilePath(
97 const std::string& directory, const std::string& hwmonBaseName,
98 const std::set<std::string>& permitSet)
Jason Ling100c20b2020-08-11 14:50:33 -070099{
100 std::optional<std::string> result;
101 std::string filename;
102 if (permitSet.empty())
103 {
104 result = directory + "/" + hwmonBaseName + "_input";
105 return result;
106 }
107 filename = directory + "/" + hwmonBaseName + "_label";
108 auto searchVal = openAndRead(filename);
109 if (!searchVal)
110 {
111 /* if the hwmon temp doesn't have a corresponding label file
112 * then use the hwmon temperature base name
113 */
114 searchVal = hwmonBaseName;
115 }
116 if (permitSet.find(*searchVal) != permitSet.end())
117 {
118 result = directory + "/" + hwmonBaseName + "_input";
119 }
120 return result;
121}
122
123/**
124 * retrieve a set of basenames and labels to allow sensor creation for.
125 * @param[in] config - a map representing the configuration for a specific
126 * device
127 * @return a set of basenames and labels to allow sensor creation for. An empty
128 * set indicates that everything is permitted.
129 */
130std::set<std::string> getPermitSet(const SensorBaseConfigMap& config)
131{
132 auto permitAttribute = config.find("Labels");
133 std::set<std::string> permitSet;
134 if (permitAttribute != config.end())
135 {
136 try
137 {
138 auto val =
139 std::get<std::vector<std::string>>(permitAttribute->second);
140
141 permitSet.insert(std::make_move_iterator(val.begin()),
142 std::make_move_iterator(val.end()));
143 }
144 catch (const std::bad_variant_access& err)
145 {
146 std::cerr << err.what()
147 << ":PermitList does not contain a list, wrong "
148 "variant type.\n";
149 }
150 }
151 return permitSet;
152}
153
James Feist6714a252018-09-10 15:26:18 -0700154bool getSensorConfiguration(
155 const std::string& type,
156 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
157 ManagedObjectType& resp, bool useCache)
158{
159 static ManagedObjectType managedObj;
Zev Weiss054aad82022-08-18 01:37:34 -0700160 std::string typeIntf = configInterfaceName(type);
James Feist6714a252018-09-10 15:26:18 -0700161
162 if (!useCache)
163 {
164 managedObj.clear();
Patrick Williams92f8f512022-07-22 19:26:55 -0500165 sdbusplus::message_t getManagedObjects =
James Feist6714a252018-09-10 15:26:18 -0700166 dbusConnection->new_method_call(
JeffLin2c5a1f22022-10-05 15:19:09 +0800167 entityManagerName, "/xyz/openbmc_project/inventory",
168 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist6714a252018-09-10 15:26:18 -0700169 try
170 {
Patrick Williams92f8f512022-07-22 19:26:55 -0500171 sdbusplus::message_t reply =
James Feist6714a252018-09-10 15:26:18 -0700172 dbusConnection->call(getManagedObjects);
Yoo, Jae Hyun0e022052018-10-15 14:05:37 -0700173 reply.read(managedObj);
James Feist6714a252018-09-10 15:26:18 -0700174 }
Patrick Williams92f8f512022-07-22 19:26:55 -0500175 catch (const sdbusplus::exception_t& e)
James Feist6714a252018-09-10 15:26:18 -0700176 {
Jason Ling5747fab2019-10-02 16:46:23 -0700177 std::cerr << "While calling GetManagedObjects on service:"
178 << entityManagerName << " exception name:" << e.name()
179 << "and description:" << e.description()
180 << " was thrown\n";
James Feist6714a252018-09-10 15:26:18 -0700181 return false;
182 }
183 }
184 for (const auto& pathPair : managedObj)
185 {
Zev Weissfd3d5f72022-08-12 18:21:02 -0700186 for (const auto& [intf, cfg] : pathPair.second)
James Feist6714a252018-09-10 15:26:18 -0700187 {
Zev Weiss054aad82022-08-18 01:37:34 -0700188 if (intf.starts_with(typeIntf))
James Feist6714a252018-09-10 15:26:18 -0700189 {
Zev Weiss79d8aef2022-08-17 21:45:44 -0700190 resp.emplace(pathPair);
James Feist6714a252018-09-10 15:26:18 -0700191 break;
192 }
193 }
James Feist6714a252018-09-10 15:26:18 -0700194 }
195 return true;
196}
197
Ed Tanous2e466962025-01-30 10:59:49 -0800198bool findFiles(const std::filesystem::path& dirPath,
199 std::string_view matchString,
200 std::vector<std::filesystem::path>& foundPaths, int symlinkDepth)
James Feist6714a252018-09-10 15:26:18 -0700201{
Lei YU6a4e9732021-10-20 13:27:34 +0800202 std::error_code ec;
Ed Tanous2e466962025-01-30 10:59:49 -0800203 if (!std::filesystem::exists(dirPath, ec))
Ed Tanous8a57ec02020-10-09 12:46:52 -0700204 {
James Feist6714a252018-09-10 15:26:18 -0700205 return false;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700206 }
James Feist6714a252018-09-10 15:26:18 -0700207
Lei YU6a4e9732021-10-20 13:27:34 +0800208 std::vector<std::regex> matchPieces;
209
210 size_t pos = 0;
211 std::string token;
212 // Generate the regex expressions list from the match we were given
213 while ((pos = matchString.find('/')) != std::string::npos)
214 {
215 token = matchString.substr(0, pos);
216 matchPieces.emplace_back(token);
217 matchString.remove_prefix(pos + 1);
218 }
219 matchPieces.emplace_back(std::string{matchString});
220
221 // Check if the match string contains directories, and skip the match of
222 // subdirectory if not
223 if (matchPieces.size() <= 1)
224 {
225 std::regex search(std::string{matchString});
226 std::smatch match;
Ed Tanous2e466962025-01-30 10:59:49 -0800227 for (auto p = std::filesystem::recursive_directory_iterator(
228 dirPath,
229 std::filesystem::directory_options::follow_directory_symlink);
230 p != std::filesystem::recursive_directory_iterator(); ++p)
Lei YU6a4e9732021-10-20 13:27:34 +0800231 {
232 std::string path = p->path().string();
233 if (!is_directory(*p))
234 {
235 if (std::regex_search(path, match, search))
236 {
237 foundPaths.emplace_back(p->path());
238 }
239 }
240 if (p.depth() >= symlinkDepth)
241 {
242 p.disable_recursion_pending();
243 }
244 }
245 return true;
246 }
247
248 // The match string contains directories, verify each level of sub
249 // directories
Ed Tanous2e466962025-01-30 10:59:49 -0800250 for (auto p = std::filesystem::recursive_directory_iterator(
251 dirPath,
252 std::filesystem::directory_options::follow_directory_symlink);
253 p != std::filesystem::recursive_directory_iterator(); ++p)
James Feist6714a252018-09-10 15:26:18 -0700254 {
Lei YU6a4e9732021-10-20 13:27:34 +0800255 std::vector<std::regex>::iterator matchPiece = matchPieces.begin();
Ed Tanous2e466962025-01-30 10:59:49 -0800256 std::filesystem::path::iterator pathIt = p->path().begin();
257 for (const std::filesystem::path& dir : dirPath)
Lei YU6a4e9732021-10-20 13:27:34 +0800258 {
259 if (dir.empty())
260 {
261 // When the path ends with '/', it gets am empty path
262 // skip such case.
263 break;
264 }
265 pathIt++;
266 }
267
268 while (pathIt != p->path().end())
269 {
270 // Found a path deeper than match.
271 if (matchPiece == matchPieces.end())
272 {
273 p.disable_recursion_pending();
274 break;
275 }
276 std::smatch match;
277 std::string component = pathIt->string();
278 std::regex regexPiece(*matchPiece);
279 if (!std::regex_match(component, match, regexPiece))
280 {
281 // path prefix doesn't match, no need to iterate further
282 p.disable_recursion_pending();
283 break;
284 }
285 matchPiece++;
286 pathIt++;
287 }
288
Ed Tanous8a57ec02020-10-09 12:46:52 -0700289 if (!is_directory(*p))
James Feist6714a252018-09-10 15:26:18 -0700290 {
Lei YU6a4e9732021-10-20 13:27:34 +0800291 if (matchPiece == matchPieces.end())
Ed Tanous8a57ec02020-10-09 12:46:52 -0700292 {
293 foundPaths.emplace_back(p->path());
294 }
James Feist6714a252018-09-10 15:26:18 -0700295 }
Lei YU6a4e9732021-10-20 13:27:34 +0800296
Ed Tanous8a57ec02020-10-09 12:46:52 -0700297 if (p.depth() >= symlinkDepth)
James Feist6714a252018-09-10 15:26:18 -0700298 {
Ed Tanous8a57ec02020-10-09 12:46:52 -0700299 p.disable_recursion_pending();
James Feist6714a252018-09-10 15:26:18 -0700300 }
301 }
302 return true;
303}
304
Ed Tanous201a1012024-04-03 18:07:28 -0700305bool isPowerOn()
James Feist6714a252018-09-10 15:26:18 -0700306{
James Feist71d31b22019-01-02 16:57:54 -0800307 if (!powerMatch)
James Feist6714a252018-09-10 15:26:18 -0700308 {
James Feist71d31b22019-01-02 16:57:54 -0800309 throw std::runtime_error("Power Match Not Created");
James Feist6714a252018-09-10 15:26:18 -0700310 }
James Feist71d31b22019-01-02 16:57:54 -0800311 return powerStatusOn;
312}
313
Ed Tanous201a1012024-04-03 18:07:28 -0700314bool hasBiosPost()
James Feistfc94b212019-02-06 16:14:51 -0800315{
James Feist52497fd2019-06-07 13:01:33 -0700316 if (!postMatch)
James Feistfc94b212019-02-06 16:14:51 -0800317 {
James Feist52497fd2019-06-07 13:01:33 -0700318 throw std::runtime_error("Post Match Not Created");
James Feistfc94b212019-02-06 16:14:51 -0800319 }
320 return biosHasPost;
321}
322
Ed Tanous201a1012024-04-03 18:07:28 -0700323bool isChassisOn()
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700324{
325 if (!chassisMatch)
326 {
327 throw std::runtime_error("Chassis On Match Not Created");
328 }
329 return chassisStatusOn;
330}
331
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000332bool readingStateGood(const PowerState& powerState)
333{
334 if (powerState == PowerState::on && !isPowerOn())
335 {
336 return false;
337 }
338 if (powerState == PowerState::biosPost && (!hasBiosPost() || !isPowerOn()))
339 {
340 return false;
341 }
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700342 if (powerState == PowerState::chassisOn && !isChassisOn())
343 {
344 return false;
345 }
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000346
347 return true;
348}
349
Patrick Williams556e04b2025-02-01 08:22:22 -0500350static void getPowerStatus(
351 const std::shared_ptr<sdbusplus::asio::connection>& conn,
352 size_t retries = 2)
James Feist8aeffd92020-09-14 12:08:28 -0700353{
354 conn->async_method_call(
355 [conn, retries](boost::system::error_code ec,
356 const std::variant<std::string>& state) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400357 if (ec)
James Feist8aeffd92020-09-14 12:08:28 -0700358 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400359 if (retries != 0U)
360 {
361 auto timer = std::make_shared<boost::asio::steady_timer>(
362 conn->get_io_context());
363 timer->expires_after(std::chrono::seconds(15));
364 timer->async_wait(
365 [timer, conn, retries](boost::system::error_code) {
366 getPowerStatus(conn, retries - 1);
367 });
368 return;
369 }
370
371 // we commonly come up before power control, we'll capture the
372 // property change later
373 std::cerr << "error getting power status " << ec.message()
374 << "\n";
James Feist8aeffd92020-09-14 12:08:28 -0700375 return;
376 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400377 powerStatusOn = std::get<std::string>(state).ends_with(".Running");
378 },
James Feist8aeffd92020-09-14 12:08:28 -0700379 power::busname, power::path, properties::interface, properties::get,
380 power::interface, power::property);
381}
382
Patrick Williams556e04b2025-02-01 08:22:22 -0500383static void getPostStatus(
384 const std::shared_ptr<sdbusplus::asio::connection>& conn,
385 size_t retries = 2)
James Feist8aeffd92020-09-14 12:08:28 -0700386{
387 conn->async_method_call(
388 [conn, retries](boost::system::error_code ec,
389 const std::variant<std::string>& state) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400390 if (ec)
James Feist8aeffd92020-09-14 12:08:28 -0700391 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400392 if (retries != 0U)
393 {
394 auto timer = std::make_shared<boost::asio::steady_timer>(
395 conn->get_io_context());
396 timer->expires_after(std::chrono::seconds(15));
397 timer->async_wait(
398 [timer, conn, retries](boost::system::error_code) {
399 getPostStatus(conn, retries - 1);
400 });
401 return;
402 }
403 // we commonly come up before power control, we'll capture the
404 // property change later
405 std::cerr << "error getting post status " << ec.message()
406 << "\n";
James Feist8aeffd92020-09-14 12:08:28 -0700407 return;
408 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400409 const auto& value = std::get<std::string>(state);
410 biosHasPost = (value != "Inactive") &&
411 (value != "xyz.openbmc_project.State.OperatingSystem."
412 "Status.OSStatus.Inactive");
413 },
James Feist8aeffd92020-09-14 12:08:28 -0700414 post::busname, post::path, properties::interface, properties::get,
415 post::interface, post::property);
416}
417
Patrick Williams556e04b2025-02-01 08:22:22 -0500418static void getChassisStatus(
419 const std::shared_ptr<sdbusplus::asio::connection>& conn,
420 size_t retries = 2)
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700421{
422 conn->async_method_call(
423 [conn, retries](boost::system::error_code ec,
424 const std::variant<std::string>& state) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400425 if (ec)
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700426 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400427 if (retries != 0U)
428 {
429 auto timer = std::make_shared<boost::asio::steady_timer>(
430 conn->get_io_context());
431 timer->expires_after(std::chrono::seconds(15));
432 timer->async_wait(
433 [timer, conn, retries](boost::system::error_code) {
434 getChassisStatus(conn, retries - 1);
435 });
436 return;
437 }
438
439 // we commonly come up before power control, we'll capture the
440 // property change later
441 std::cerr << "error getting chassis power status "
442 << ec.message() << "\n";
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700443 return;
444 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400445 chassisStatusOn =
446 std::get<std::string>(state).ends_with(chassis::sOn);
447 },
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700448 chassis::busname, chassis::path, properties::interface, properties::get,
449 chassis::interface, chassis::property);
450}
451
Zev Weiss88cb29d2022-05-09 03:46:15 +0000452void setupPowerMatchCallback(
453 const std::shared_ptr<sdbusplus::asio::connection>& conn,
454 std::function<void(PowerState type, bool state)>&& hostStatusCallback)
James Feist71d31b22019-01-02 16:57:54 -0800455{
James Feist43d32fe2019-09-04 10:35:20 -0700456 static boost::asio::steady_timer timer(conn->get_io_context());
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700457 static boost::asio::steady_timer timerChassisOn(conn->get_io_context());
James Feist6714a252018-09-10 15:26:18 -0700458 // create a match for powergood changes, first time do a method call to
James Feist71d31b22019-01-02 16:57:54 -0800459 // cache the correct value
James Feist52497fd2019-06-07 13:01:33 -0700460 if (powerMatch)
461 {
462 return;
463 }
James Feist6714a252018-09-10 15:26:18 -0700464
Patrick Williams92f8f512022-07-22 19:26:55 -0500465 powerMatch = std::make_unique<sdbusplus::bus::match_t>(
466 static_cast<sdbusplus::bus_t&>(*conn),
James Feist52497fd2019-06-07 13:01:33 -0700467 "type='signal',interface='" + std::string(properties::interface) +
468 "',path='" + std::string(power::path) + "',arg0='" +
469 std::string(power::interface) + "'",
Zev Weiss88cb29d2022-05-09 03:46:15 +0000470 [hostStatusCallback](sdbusplus::message_t& message) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400471 std::string objectName;
472 boost::container::flat_map<std::string, std::variant<std::string>>
473 values;
474 message.read(objectName, values);
475 auto findState = values.find(power::property);
476 if (findState != values.end())
James Feist6714a252018-09-10 15:26:18 -0700477 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400478 bool on = std::get<std::string>(findState->second)
479 .ends_with(".Running");
480 if (!on)
481 {
482 timer.cancel();
483 powerStatusOn = false;
484 hostStatusCallback(PowerState::on, powerStatusOn);
485 return;
486 }
487 // on comes too quickly
488 timer.expires_after(std::chrono::seconds(10));
489 timer.async_wait(
490 [hostStatusCallback](boost::system::error_code ec) {
491 if (ec == boost::asio::error::operation_aborted)
492 {
493 return;
494 }
495 if (ec)
496 {
497 std::cerr << "Timer error " << ec.message() << "\n";
498 return;
499 }
500 powerStatusOn = true;
501 hostStatusCallback(PowerState::on, powerStatusOn);
502 });
Ed Tanousbb679322022-05-16 16:10:00 -0700503 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400504 });
James Feist52497fd2019-06-07 13:01:33 -0700505
Patrick Williams92f8f512022-07-22 19:26:55 -0500506 postMatch = std::make_unique<sdbusplus::bus::match_t>(
507 static_cast<sdbusplus::bus_t&>(*conn),
James Feist52497fd2019-06-07 13:01:33 -0700508 "type='signal',interface='" + std::string(properties::interface) +
509 "',path='" + std::string(post::path) + "',arg0='" +
510 std::string(post::interface) + "'",
Zev Weiss88cb29d2022-05-09 03:46:15 +0000511 [hostStatusCallback](sdbusplus::message_t& message) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400512 std::string objectName;
513 boost::container::flat_map<std::string, std::variant<std::string>>
514 values;
515 message.read(objectName, values);
516 auto findState = values.find(post::property);
517 if (findState != values.end())
518 {
519 auto& value = std::get<std::string>(findState->second);
520 biosHasPost =
521 (value != "Inactive") &&
522 (value != "xyz.openbmc_project.State.OperatingSystem."
523 "Status.OSStatus.Inactive");
524 hostStatusCallback(PowerState::biosPost, biosHasPost);
525 }
526 });
James Feistfc94b212019-02-06 16:14:51 -0800527
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700528 chassisMatch = std::make_unique<sdbusplus::bus::match_t>(
529 static_cast<sdbusplus::bus_t&>(*conn),
530 "type='signal',interface='" + std::string(properties::interface) +
531 "',path='" + std::string(chassis::path) + "',arg0='" +
532 std::string(chassis::interface) + "'",
Ed Tanous91423432024-04-04 08:34:21 -0700533 [hostStatusCallback = std::move(hostStatusCallback)](
534 sdbusplus::message_t& message) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400535 std::string objectName;
536 boost::container::flat_map<std::string, std::variant<std::string>>
537 values;
538 message.read(objectName, values);
539 auto findState = values.find(chassis::property);
540 if (findState != values.end())
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700541 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400542 bool on = std::get<std::string>(findState->second)
543 .ends_with(chassis::sOn);
544 if (!on)
545 {
546 timerChassisOn.cancel();
547 chassisStatusOn = false;
548 hostStatusCallback(PowerState::chassisOn, chassisStatusOn);
549 return;
550 }
551 // on comes too quickly
552 timerChassisOn.expires_after(std::chrono::seconds(10));
553 timerChassisOn.async_wait([hostStatusCallback](
554 boost::system::error_code ec) {
555 if (ec == boost::asio::error::operation_aborted)
556 {
557 return;
558 }
559 if (ec)
560 {
561 std::cerr << "Timer error " << ec.message() << "\n";
562 return;
563 }
564 chassisStatusOn = true;
565 hostStatusCallback(PowerState::chassisOn, chassisStatusOn);
566 });
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700567 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400568 });
James Feist8aeffd92020-09-14 12:08:28 -0700569 getPowerStatus(conn);
570 getPostStatus(conn);
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700571 getChassisStatus(conn);
James Feist3f0e8762018-11-27 11:30:42 -0800572}
James Feist87d713a2018-12-06 16:06:24 -0800573
Zev Weiss88cb29d2022-05-09 03:46:15 +0000574void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
575{
576 setupPowerMatchCallback(conn, [](PowerState, bool) {});
577}
578
James Feist87d713a2018-12-06 16:06:24 -0800579// replaces limits if MinReading and MaxReading are found.
580void findLimits(std::pair<double, double>& limits,
581 const SensorBaseConfiguration* data)
582{
Ed Tanous2049bd22022-07-09 07:20:26 -0700583 if (data == nullptr)
James Feist87d713a2018-12-06 16:06:24 -0800584 {
585 return;
586 }
587 auto maxFind = data->second.find("MaxReading");
588 auto minFind = data->second.find("MinReading");
589
590 if (minFind != data->second.end())
591 {
James Feist3eb82622019-02-08 13:10:22 -0800592 limits.first = std::visit(VariantToDoubleVisitor(), minFind->second);
James Feist87d713a2018-12-06 16:06:24 -0800593 }
594 if (maxFind != data->second.end())
595 {
James Feist3eb82622019-02-08 13:10:22 -0800596 limits.second = std::visit(VariantToDoubleVisitor(), maxFind->second);
James Feist87d713a2018-12-06 16:06:24 -0800597 }
James Feistfc94b212019-02-06 16:14:51 -0800598}
James Feist82bac4c2019-03-11 11:16:53 -0700599
600void createAssociation(
601 std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
602 const std::string& path)
603{
604 if (association)
605 {
Ed Tanous2e466962025-01-30 10:59:49 -0800606 std::filesystem::path p(path);
James Feistd2543f82019-04-09 11:10:06 -0700607
James Feist82bac4c2019-03-11 11:16:53 -0700608 std::vector<Association> associations;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700609 associations.emplace_back("chassis", "all_sensors",
610 p.parent_path().string());
James Feist2adc95c2019-09-30 14:55:28 -0700611 association->register_property("Associations", associations);
James Feist82bac4c2019-03-11 11:16:53 -0700612 association->initialize();
613 }
James Feist43d32fe2019-09-04 10:35:20 -0700614}
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800615
616void setInventoryAssociation(
Ed Tanous8a57ec02020-10-09 12:46:52 -0700617 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000618 const std::string& inventoryPath, const std::string& chassisPath)
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800619{
620 if (association)
621 {
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800622 std::vector<Association> associations;
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000623 associations.emplace_back("inventory", "sensors", inventoryPath);
624 associations.emplace_back("chassis", "all_sensors", chassisPath);
AppaRao Pulic82213c2020-02-27 01:24:58 +0530625
James Feist2adc95c2019-09-30 14:55:28 -0700626 association->register_property("Associations", associations);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800627 association->initialize();
628 }
629}
630
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000631std::optional<std::string> findContainingChassis(std::string_view configParent,
632 const GetSubTreeType& subtree)
633{
634 // A parent that is a chassis takes precedence
635 for (const auto& [obj, services] : subtree)
636 {
637 if (obj == configParent)
638 {
639 return obj;
640 }
641 }
642
643 // If the parent is not a chassis, the system chassis is used. This does not
644 // work if there is more than one System, but we assume there is only one
645 // today.
646 for (const auto& [obj, services] : subtree)
647 {
648 for (const auto& [service, interfaces] : services)
649 {
650 if (std::find(interfaces.begin(), interfaces.end(),
651 "xyz.openbmc_project.Inventory.Item.System") !=
652 interfaces.end())
653 {
654 return obj;
655 }
656 }
657 }
658 return std::nullopt;
659}
660
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800661void createInventoryAssoc(
Ed Tanous8a57ec02020-10-09 12:46:52 -0700662 const std::shared_ptr<sdbusplus::asio::connection>& conn,
663 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800664 const std::string& path)
665{
666 if (!association)
667 {
668 return;
669 }
AppaRao Pulic82213c2020-02-27 01:24:58 +0530670
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000671 constexpr auto allInterfaces = std::to_array({
672 "xyz.openbmc_project.Inventory.Item.Board",
673 "xyz.openbmc_project.Inventory.Item.Chassis",
674 });
675
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800676 conn->async_method_call(
677 [association, path](const boost::system::error_code ec,
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000678 const GetSubTreeType& subtree) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400679 // The parent of the config is always the inventory object, and may
680 // be the associated chassis. If the parent is not itself a chassis
681 // or board, the sensor is associated with the system chassis.
Ed Tanous2e466962025-01-30 10:59:49 -0800682 std::string parent =
683 std::filesystem::path(path).parent_path().string();
Patrick Williams2aaf7172024-08-16 15:20:40 -0400684 if (ec)
685 {
686 // In case of error, set the default associations and
687 // initialize the association Interface.
688 setInventoryAssociation(association, parent, parent);
689 return;
690 }
691 setInventoryAssociation(
692 association, parent,
693 findContainingChassis(parent, subtree).value_or(parent));
694 },
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000695 mapper::busName, mapper::path, mapper::interface, "GetSubTree",
696 "/xyz/openbmc_project/inventory/system", 2, allInterfaces);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800697}
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200698
699std::optional<double> readFile(const std::string& thresholdFile,
700 const double& scaleFactor)
701{
702 std::string line;
703 std::ifstream labelFile(thresholdFile);
704 if (labelFile.good())
705 {
706 std::getline(labelFile, line);
707 labelFile.close();
708
709 try
710 {
711 return std::stod(line) / scaleFactor;
712 }
713 catch (const std::invalid_argument&)
714 {
715 return std::nullopt;
716 }
717 }
718 return std::nullopt;
719}
720
Patrick Williams556e04b2025-02-01 08:22:22 -0500721std::optional<std::tuple<std::string, std::string, std::string>> splitFileName(
722 const std::filesystem::path& filePath)
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200723{
724 if (filePath.has_filename())
725 {
726 const auto fileName = filePath.filename().string();
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200727
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200728 size_t numberPos = std::strcspn(fileName.c_str(), "1234567890");
729 size_t itemPos = std::strcspn(fileName.c_str(), "_");
730
731 if (numberPos > 0 && itemPos > numberPos && fileName.size() > itemPos)
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200732 {
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200733 return std::make_optional(
734 std::make_tuple(fileName.substr(0, numberPos),
735 fileName.substr(numberPos, itemPos - numberPos),
736 fileName.substr(itemPos + 1, fileName.size())));
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200737 }
738 }
739 return std::nullopt;
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200740}
Bruce Lee1263c3d2021-06-04 15:16:33 +0800741
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530742static void handleSpecialModeChange(const std::string& manufacturingModeStatus)
743{
744 manufacturingMode = false;
745 if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security."
746 "SpecialMode.Modes.Manufacturing")
747 {
748 manufacturingMode = true;
749 }
Ed Tanous74cffa82022-01-25 13:00:28 -0800750 if (validateUnsecureFeature == 1)
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530751 {
752 if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security."
753 "SpecialMode.Modes.ValidationUnsecure")
754 {
755 manufacturingMode = true;
756 }
757 }
758}
759
Bruce Lee1263c3d2021-06-04 15:16:33 +0800760void setupManufacturingModeMatch(sdbusplus::asio::connection& conn)
761{
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530762 namespace rules = sdbusplus::bus::match::rules;
763 static constexpr const char* specialModeInterface =
764 "xyz.openbmc_project.Security.SpecialMode";
765
766 const std::string filterSpecialModeIntfAdd =
767 rules::interfacesAdded() +
768 rules::argNpath(0, "/xyz/openbmc_project/security/special_mode");
Patrick Williams92f8f512022-07-22 19:26:55 -0500769 static std::unique_ptr<sdbusplus::bus::match_t> specialModeIntfMatch =
Patrick Williams597e8422023-10-20 11:19:01 -0500770 std::make_unique<sdbusplus::bus::match_t>(
771 conn, filterSpecialModeIntfAdd, [](sdbusplus::message_t& m) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400772 sdbusplus::message::object_path path;
773 using PropertyMap =
774 boost::container::flat_map<std::string,
775 std::variant<std::string>>;
776 boost::container::flat_map<std::string, PropertyMap>
777 interfaceAdded;
778 m.read(path, interfaceAdded);
779 auto intfItr = interfaceAdded.find(specialModeInterface);
780 if (intfItr == interfaceAdded.end())
781 {
782 return;
783 }
784 PropertyMap& propertyList = intfItr->second;
785 auto itr = propertyList.find("SpecialMode");
786 if (itr == propertyList.end())
787 {
788 std::cerr << "error getting SpecialMode property "
789 << "\n";
790 return;
791 }
792 auto* manufacturingModeStatus =
793 std::get_if<std::string>(&itr->second);
794 handleSpecialModeChange(*manufacturingModeStatus);
795 });
Bruce Lee1263c3d2021-06-04 15:16:33 +0800796
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530797 const std::string filterSpecialModeChange =
798 rules::type::signal() + rules::member("PropertiesChanged") +
799 rules::interface("org.freedesktop.DBus.Properties") +
800 rules::argN(0, specialModeInterface);
Patrick Williams92f8f512022-07-22 19:26:55 -0500801 static std::unique_ptr<sdbusplus::bus::match_t> specialModeChangeMatch =
Patrick Williams2aaf7172024-08-16 15:20:40 -0400802 std::make_unique<sdbusplus::bus::match_t>(
803 conn, filterSpecialModeChange, [](sdbusplus::message_t& m) {
804 std::string interfaceName;
805 boost::container::flat_map<std::string,
806 std::variant<std::string>>
807 propertiesChanged;
Bruce Lee1263c3d2021-06-04 15:16:33 +0800808
Patrick Williams2aaf7172024-08-16 15:20:40 -0400809 m.read(interfaceName, propertiesChanged);
810 auto itr = propertiesChanged.find("SpecialMode");
811 if (itr == propertiesChanged.end())
812 {
813 return;
814 }
815 auto* manufacturingModeStatus =
816 std::get_if<std::string>(&itr->second);
817 handleSpecialModeChange(*manufacturingModeStatus);
818 });
Bruce Lee1263c3d2021-06-04 15:16:33 +0800819
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530820 conn.async_method_call(
821 [](const boost::system::error_code ec,
822 const std::variant<std::string>& getManufactMode) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400823 if (ec)
824 {
825 std::cerr << "error getting SpecialMode status "
826 << ec.message() << "\n";
827 return;
828 }
829 const auto* manufacturingModeStatus =
830 std::get_if<std::string>(&getManufactMode);
831 handleSpecialModeChange(*manufacturingModeStatus);
832 },
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530833 "xyz.openbmc_project.SpecialMode",
834 "/xyz/openbmc_project/security/special_mode",
835 "org.freedesktop.DBus.Properties", "Get", specialModeInterface,
836 "SpecialMode");
Bruce Lee1263c3d2021-06-04 15:16:33 +0800837}
838
839bool getManufacturingMode()
840{
841 return manufacturingMode;
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000842}
Zev Weiss214d9712022-08-12 12:54:31 -0700843
844std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
845 setupPropertiesChangedMatches(
846 sdbusplus::asio::connection& bus, std::span<const char* const> types,
847 const std::function<void(sdbusplus::message_t&)>& handler)
848{
849 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches;
850 for (const char* type : types)
851 {
852 auto match = std::make_unique<sdbusplus::bus::match_t>(
853 static_cast<sdbusplus::bus_t&>(bus),
854 "type='signal',member='PropertiesChanged',path_namespace='" +
Zev Weiss054aad82022-08-18 01:37:34 -0700855 std::string(inventoryPath) + "',arg0namespace='" +
856 configInterfaceName(type) + "'",
Zev Weiss214d9712022-08-12 12:54:31 -0700857 handler);
858 matches.emplace_back(std::move(match));
859 }
860 return matches;
861}
Zev Weissdabd48d2022-08-03 15:43:17 -0700862
863std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
864 setupPropertiesChangedMatches(
865 sdbusplus::asio::connection& bus, const I2CDeviceTypeMap& typeMap,
866 const std::function<void(sdbusplus::message_t&)>& handler)
867{
868 std::vector<const char*> types;
869 types.reserve(typeMap.size());
870 for (const auto& [type, dt] : typeMap)
871 {
872 types.push_back(type.data());
873 }
874 return setupPropertiesChangedMatches(bus, {types}, handler);
875}