blob: 202e3946d586769a0e8b91a1a69a46c2c9bcd0cd [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>
George Liu61984352025-02-24 14:47:34 +080027#include <phosphor-logging/lg2.hpp>
James Feist38fb5982020-05-28 10:09:54 -070028#include <sdbusplus/asio/connection.hpp>
29#include <sdbusplus/asio/object_server.hpp>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070030#include <sdbusplus/bus.hpp>
James Feist38fb5982020-05-28 10:09:54 -070031#include <sdbusplus/bus/match.hpp>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070032#include <sdbusplus/exception.hpp>
33#include <sdbusplus/message.hpp>
34#include <sdbusplus/message/native_types.hpp>
James Feist38fb5982020-05-28 10:09:54 -070035
Ed Tanouseacbfdd2024-04-04 12:00:24 -070036#include <algorithm>
37#include <array>
38#include <chrono>
39#include <cstddef>
40#include <cstring>
James Feist24f02f22019-04-15 11:05:39 -070041#include <filesystem>
James Feist6714a252018-09-10 15:26:18 -070042#include <fstream>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070043#include <functional>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070044#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 {
George Liu61984352025-02-24 14:47:34 +0800146 lg2::error(
147 "'{ERROR_MESSAGE}': PermitList does not contain a list, wrong variant type.",
148 "ERROR_MESSAGE", err.what());
Jason Ling100c20b2020-08-11 14:50:33 -0700149 }
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 {
George Liu61984352025-02-24 14:47:34 +0800177 lg2::error(
178 "While calling GetManagedObjects on service: '{SERVICE_NAME}'"
179 " exception name: '{EXCEPTION_NAME}' and description: "
180 "'{EXCEPTION_DESCRIPTION}' was thrown",
181 "SERVICE_NAME", entityManagerName, "EXCEPTION_NAME", e.name(),
182 "EXCEPTION_DESCRIPTION", e.description());
James Feist6714a252018-09-10 15:26:18 -0700183 return false;
184 }
185 }
186 for (const auto& pathPair : managedObj)
187 {
Zev Weissfd3d5f72022-08-12 18:21:02 -0700188 for (const auto& [intf, cfg] : pathPair.second)
James Feist6714a252018-09-10 15:26:18 -0700189 {
Zev Weiss054aad82022-08-18 01:37:34 -0700190 if (intf.starts_with(typeIntf))
James Feist6714a252018-09-10 15:26:18 -0700191 {
Zev Weiss79d8aef2022-08-17 21:45:44 -0700192 resp.emplace(pathPair);
James Feist6714a252018-09-10 15:26:18 -0700193 break;
194 }
195 }
James Feist6714a252018-09-10 15:26:18 -0700196 }
197 return true;
198}
199
Ed Tanous2e466962025-01-30 10:59:49 -0800200bool findFiles(const std::filesystem::path& dirPath,
201 std::string_view matchString,
202 std::vector<std::filesystem::path>& foundPaths, int symlinkDepth)
James Feist6714a252018-09-10 15:26:18 -0700203{
Lei YU6a4e9732021-10-20 13:27:34 +0800204 std::error_code ec;
Ed Tanous2e466962025-01-30 10:59:49 -0800205 if (!std::filesystem::exists(dirPath, ec))
Ed Tanous8a57ec02020-10-09 12:46:52 -0700206 {
James Feist6714a252018-09-10 15:26:18 -0700207 return false;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700208 }
James Feist6714a252018-09-10 15:26:18 -0700209
Lei YU6a4e9732021-10-20 13:27:34 +0800210 std::vector<std::regex> matchPieces;
211
212 size_t pos = 0;
213 std::string token;
214 // Generate the regex expressions list from the match we were given
215 while ((pos = matchString.find('/')) != std::string::npos)
216 {
217 token = matchString.substr(0, pos);
218 matchPieces.emplace_back(token);
219 matchString.remove_prefix(pos + 1);
220 }
221 matchPieces.emplace_back(std::string{matchString});
222
223 // Check if the match string contains directories, and skip the match of
224 // subdirectory if not
225 if (matchPieces.size() <= 1)
226 {
227 std::regex search(std::string{matchString});
228 std::smatch match;
Ed Tanous2e466962025-01-30 10:59:49 -0800229 for (auto p = std::filesystem::recursive_directory_iterator(
230 dirPath,
231 std::filesystem::directory_options::follow_directory_symlink);
232 p != std::filesystem::recursive_directory_iterator(); ++p)
Lei YU6a4e9732021-10-20 13:27:34 +0800233 {
234 std::string path = p->path().string();
235 if (!is_directory(*p))
236 {
237 if (std::regex_search(path, match, search))
238 {
239 foundPaths.emplace_back(p->path());
240 }
241 }
242 if (p.depth() >= symlinkDepth)
243 {
244 p.disable_recursion_pending();
245 }
246 }
247 return true;
248 }
249
250 // The match string contains directories, verify each level of sub
251 // directories
Ed Tanous2e466962025-01-30 10:59:49 -0800252 for (auto p = std::filesystem::recursive_directory_iterator(
253 dirPath,
254 std::filesystem::directory_options::follow_directory_symlink);
255 p != std::filesystem::recursive_directory_iterator(); ++p)
James Feist6714a252018-09-10 15:26:18 -0700256 {
Lei YU6a4e9732021-10-20 13:27:34 +0800257 std::vector<std::regex>::iterator matchPiece = matchPieces.begin();
Ed Tanous2e466962025-01-30 10:59:49 -0800258 std::filesystem::path::iterator pathIt = p->path().begin();
259 for (const std::filesystem::path& dir : dirPath)
Lei YU6a4e9732021-10-20 13:27:34 +0800260 {
261 if (dir.empty())
262 {
263 // When the path ends with '/', it gets am empty path
264 // skip such case.
265 break;
266 }
267 pathIt++;
268 }
269
270 while (pathIt != p->path().end())
271 {
272 // Found a path deeper than match.
273 if (matchPiece == matchPieces.end())
274 {
275 p.disable_recursion_pending();
276 break;
277 }
278 std::smatch match;
279 std::string component = pathIt->string();
280 std::regex regexPiece(*matchPiece);
281 if (!std::regex_match(component, match, regexPiece))
282 {
283 // path prefix doesn't match, no need to iterate further
284 p.disable_recursion_pending();
285 break;
286 }
287 matchPiece++;
288 pathIt++;
289 }
290
Ed Tanous8a57ec02020-10-09 12:46:52 -0700291 if (!is_directory(*p))
James Feist6714a252018-09-10 15:26:18 -0700292 {
Lei YU6a4e9732021-10-20 13:27:34 +0800293 if (matchPiece == matchPieces.end())
Ed Tanous8a57ec02020-10-09 12:46:52 -0700294 {
295 foundPaths.emplace_back(p->path());
296 }
James Feist6714a252018-09-10 15:26:18 -0700297 }
Lei YU6a4e9732021-10-20 13:27:34 +0800298
Ed Tanous8a57ec02020-10-09 12:46:52 -0700299 if (p.depth() >= symlinkDepth)
James Feist6714a252018-09-10 15:26:18 -0700300 {
Ed Tanous8a57ec02020-10-09 12:46:52 -0700301 p.disable_recursion_pending();
James Feist6714a252018-09-10 15:26:18 -0700302 }
303 }
304 return true;
305}
306
Ed Tanous201a1012024-04-03 18:07:28 -0700307bool isPowerOn()
James Feist6714a252018-09-10 15:26:18 -0700308{
James Feist71d31b22019-01-02 16:57:54 -0800309 if (!powerMatch)
James Feist6714a252018-09-10 15:26:18 -0700310 {
James Feist71d31b22019-01-02 16:57:54 -0800311 throw std::runtime_error("Power Match Not Created");
James Feist6714a252018-09-10 15:26:18 -0700312 }
James Feist71d31b22019-01-02 16:57:54 -0800313 return powerStatusOn;
314}
315
Ed Tanous201a1012024-04-03 18:07:28 -0700316bool hasBiosPost()
James Feistfc94b212019-02-06 16:14:51 -0800317{
James Feist52497fd2019-06-07 13:01:33 -0700318 if (!postMatch)
James Feistfc94b212019-02-06 16:14:51 -0800319 {
James Feist52497fd2019-06-07 13:01:33 -0700320 throw std::runtime_error("Post Match Not Created");
James Feistfc94b212019-02-06 16:14:51 -0800321 }
322 return biosHasPost;
323}
324
Ed Tanous201a1012024-04-03 18:07:28 -0700325bool isChassisOn()
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700326{
327 if (!chassisMatch)
328 {
329 throw std::runtime_error("Chassis On Match Not Created");
330 }
331 return chassisStatusOn;
332}
333
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000334bool readingStateGood(const PowerState& powerState)
335{
336 if (powerState == PowerState::on && !isPowerOn())
337 {
338 return false;
339 }
340 if (powerState == PowerState::biosPost && (!hasBiosPost() || !isPowerOn()))
341 {
342 return false;
343 }
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700344 if (powerState == PowerState::chassisOn && !isChassisOn())
345 {
346 return false;
347 }
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000348
349 return true;
350}
351
Patrick Williams556e04b2025-02-01 08:22:22 -0500352static void getPowerStatus(
353 const std::shared_ptr<sdbusplus::asio::connection>& conn,
354 size_t retries = 2)
James Feist8aeffd92020-09-14 12:08:28 -0700355{
356 conn->async_method_call(
357 [conn, retries](boost::system::error_code ec,
358 const std::variant<std::string>& state) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400359 if (ec)
James Feist8aeffd92020-09-14 12:08:28 -0700360 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400361 if (retries != 0U)
362 {
363 auto timer = std::make_shared<boost::asio::steady_timer>(
364 conn->get_io_context());
365 timer->expires_after(std::chrono::seconds(15));
366 timer->async_wait(
367 [timer, conn, retries](boost::system::error_code) {
368 getPowerStatus(conn, retries - 1);
369 });
370 return;
371 }
372
373 // we commonly come up before power control, we'll capture the
374 // property change later
George Liu61984352025-02-24 14:47:34 +0800375 lg2::error("error getting power status: '{ERROR_MESSAGE}'",
376 "ERROR_MESSAGE", ec.message());
James Feist8aeffd92020-09-14 12:08:28 -0700377 return;
378 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400379 powerStatusOn = std::get<std::string>(state).ends_with(".Running");
380 },
James Feist8aeffd92020-09-14 12:08:28 -0700381 power::busname, power::path, properties::interface, properties::get,
382 power::interface, power::property);
383}
384
Patrick Williams556e04b2025-02-01 08:22:22 -0500385static void getPostStatus(
386 const std::shared_ptr<sdbusplus::asio::connection>& conn,
387 size_t retries = 2)
James Feist8aeffd92020-09-14 12:08:28 -0700388{
389 conn->async_method_call(
390 [conn, retries](boost::system::error_code ec,
391 const std::variant<std::string>& state) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400392 if (ec)
James Feist8aeffd92020-09-14 12:08:28 -0700393 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400394 if (retries != 0U)
395 {
396 auto timer = std::make_shared<boost::asio::steady_timer>(
397 conn->get_io_context());
398 timer->expires_after(std::chrono::seconds(15));
399 timer->async_wait(
400 [timer, conn, retries](boost::system::error_code) {
401 getPostStatus(conn, retries - 1);
402 });
403 return;
404 }
405 // we commonly come up before power control, we'll capture the
406 // property change later
George Liu61984352025-02-24 14:47:34 +0800407 lg2::error("error getting post status: '{ERROR_MESSAGE}'",
408 "ERROR_MESSAGE", ec.message());
James Feist8aeffd92020-09-14 12:08:28 -0700409 return;
410 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400411 const auto& value = std::get<std::string>(state);
412 biosHasPost = (value != "Inactive") &&
413 (value != "xyz.openbmc_project.State.OperatingSystem."
414 "Status.OSStatus.Inactive");
415 },
James Feist8aeffd92020-09-14 12:08:28 -0700416 post::busname, post::path, properties::interface, properties::get,
417 post::interface, post::property);
418}
419
Patrick Williams556e04b2025-02-01 08:22:22 -0500420static void getChassisStatus(
421 const std::shared_ptr<sdbusplus::asio::connection>& conn,
422 size_t retries = 2)
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700423{
424 conn->async_method_call(
425 [conn, retries](boost::system::error_code ec,
426 const std::variant<std::string>& state) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400427 if (ec)
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700428 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400429 if (retries != 0U)
430 {
431 auto timer = std::make_shared<boost::asio::steady_timer>(
432 conn->get_io_context());
433 timer->expires_after(std::chrono::seconds(15));
434 timer->async_wait(
435 [timer, conn, retries](boost::system::error_code) {
436 getChassisStatus(conn, retries - 1);
437 });
438 return;
439 }
440
441 // we commonly come up before power control, we'll capture the
442 // property change later
George Liu61984352025-02-24 14:47:34 +0800443 lg2::error(
444 "error getting chassis power status: '{ERROR_MESSAGE}'",
445 "ERROR_MESSAGE", ec.message());
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700446 return;
447 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400448 chassisStatusOn =
449 std::get<std::string>(state).ends_with(chassis::sOn);
450 },
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700451 chassis::busname, chassis::path, properties::interface, properties::get,
452 chassis::interface, chassis::property);
453}
454
Zev Weiss88cb29d2022-05-09 03:46:15 +0000455void setupPowerMatchCallback(
456 const std::shared_ptr<sdbusplus::asio::connection>& conn,
457 std::function<void(PowerState type, bool state)>&& hostStatusCallback)
James Feist71d31b22019-01-02 16:57:54 -0800458{
James Feist43d32fe2019-09-04 10:35:20 -0700459 static boost::asio::steady_timer timer(conn->get_io_context());
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700460 static boost::asio::steady_timer timerChassisOn(conn->get_io_context());
James Feist6714a252018-09-10 15:26:18 -0700461 // create a match for powergood changes, first time do a method call to
James Feist71d31b22019-01-02 16:57:54 -0800462 // cache the correct value
James Feist52497fd2019-06-07 13:01:33 -0700463 if (powerMatch)
464 {
465 return;
466 }
James Feist6714a252018-09-10 15:26:18 -0700467
Patrick Williams92f8f512022-07-22 19:26:55 -0500468 powerMatch = std::make_unique<sdbusplus::bus::match_t>(
469 static_cast<sdbusplus::bus_t&>(*conn),
James Feist52497fd2019-06-07 13:01:33 -0700470 "type='signal',interface='" + std::string(properties::interface) +
471 "',path='" + std::string(power::path) + "',arg0='" +
472 std::string(power::interface) + "'",
Zev Weiss88cb29d2022-05-09 03:46:15 +0000473 [hostStatusCallback](sdbusplus::message_t& message) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400474 std::string objectName;
475 boost::container::flat_map<std::string, std::variant<std::string>>
476 values;
477 message.read(objectName, values);
478 auto findState = values.find(power::property);
479 if (findState != values.end())
James Feist6714a252018-09-10 15:26:18 -0700480 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400481 bool on = std::get<std::string>(findState->second)
482 .ends_with(".Running");
483 if (!on)
484 {
485 timer.cancel();
486 powerStatusOn = false;
487 hostStatusCallback(PowerState::on, powerStatusOn);
488 return;
489 }
490 // on comes too quickly
491 timer.expires_after(std::chrono::seconds(10));
492 timer.async_wait(
493 [hostStatusCallback](boost::system::error_code ec) {
494 if (ec == boost::asio::error::operation_aborted)
495 {
496 return;
497 }
498 if (ec)
499 {
George Liu61984352025-02-24 14:47:34 +0800500 lg2::error("Timer error: '{ERROR_MESSAGE}'",
501 "ERROR_MESSAGE", ec.message());
Patrick Williams2aaf7172024-08-16 15:20:40 -0400502 return;
503 }
504 powerStatusOn = true;
505 hostStatusCallback(PowerState::on, powerStatusOn);
506 });
Ed Tanousbb679322022-05-16 16:10:00 -0700507 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400508 });
James Feist52497fd2019-06-07 13:01:33 -0700509
Patrick Williams92f8f512022-07-22 19:26:55 -0500510 postMatch = std::make_unique<sdbusplus::bus::match_t>(
511 static_cast<sdbusplus::bus_t&>(*conn),
James Feist52497fd2019-06-07 13:01:33 -0700512 "type='signal',interface='" + std::string(properties::interface) +
513 "',path='" + std::string(post::path) + "',arg0='" +
514 std::string(post::interface) + "'",
Zev Weiss88cb29d2022-05-09 03:46:15 +0000515 [hostStatusCallback](sdbusplus::message_t& message) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400516 std::string objectName;
517 boost::container::flat_map<std::string, std::variant<std::string>>
518 values;
519 message.read(objectName, values);
520 auto findState = values.find(post::property);
521 if (findState != values.end())
522 {
523 auto& value = std::get<std::string>(findState->second);
524 biosHasPost =
525 (value != "Inactive") &&
526 (value != "xyz.openbmc_project.State.OperatingSystem."
527 "Status.OSStatus.Inactive");
528 hostStatusCallback(PowerState::biosPost, biosHasPost);
529 }
530 });
James Feistfc94b212019-02-06 16:14:51 -0800531
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700532 chassisMatch = std::make_unique<sdbusplus::bus::match_t>(
533 static_cast<sdbusplus::bus_t&>(*conn),
534 "type='signal',interface='" + std::string(properties::interface) +
535 "',path='" + std::string(chassis::path) + "',arg0='" +
536 std::string(chassis::interface) + "'",
Ed Tanous91423432024-04-04 08:34:21 -0700537 [hostStatusCallback = std::move(hostStatusCallback)](
538 sdbusplus::message_t& message) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400539 std::string objectName;
540 boost::container::flat_map<std::string, std::variant<std::string>>
541 values;
542 message.read(objectName, values);
543 auto findState = values.find(chassis::property);
544 if (findState != values.end())
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700545 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400546 bool on = std::get<std::string>(findState->second)
547 .ends_with(chassis::sOn);
548 if (!on)
549 {
550 timerChassisOn.cancel();
551 chassisStatusOn = false;
552 hostStatusCallback(PowerState::chassisOn, chassisStatusOn);
553 return;
554 }
555 // on comes too quickly
556 timerChassisOn.expires_after(std::chrono::seconds(10));
557 timerChassisOn.async_wait([hostStatusCallback](
558 boost::system::error_code ec) {
559 if (ec == boost::asio::error::operation_aborted)
560 {
561 return;
562 }
563 if (ec)
564 {
George Liu61984352025-02-24 14:47:34 +0800565 lg2::error("Timer error: '{ERROR_MESSAGE}'",
566 "ERROR_MESSAGE", ec.message());
Patrick Williams2aaf7172024-08-16 15:20:40 -0400567 return;
568 }
569 chassisStatusOn = true;
570 hostStatusCallback(PowerState::chassisOn, chassisStatusOn);
571 });
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700572 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400573 });
James Feist8aeffd92020-09-14 12:08:28 -0700574 getPowerStatus(conn);
575 getPostStatus(conn);
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700576 getChassisStatus(conn);
James Feist3f0e8762018-11-27 11:30:42 -0800577}
James Feist87d713a2018-12-06 16:06:24 -0800578
Zev Weiss88cb29d2022-05-09 03:46:15 +0000579void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
580{
581 setupPowerMatchCallback(conn, [](PowerState, bool) {});
582}
583
James Feist87d713a2018-12-06 16:06:24 -0800584// replaces limits if MinReading and MaxReading are found.
585void findLimits(std::pair<double, double>& limits,
586 const SensorBaseConfiguration* data)
587{
Ed Tanous2049bd22022-07-09 07:20:26 -0700588 if (data == nullptr)
James Feist87d713a2018-12-06 16:06:24 -0800589 {
590 return;
591 }
592 auto maxFind = data->second.find("MaxReading");
593 auto minFind = data->second.find("MinReading");
594
595 if (minFind != data->second.end())
596 {
James Feist3eb82622019-02-08 13:10:22 -0800597 limits.first = std::visit(VariantToDoubleVisitor(), minFind->second);
James Feist87d713a2018-12-06 16:06:24 -0800598 }
599 if (maxFind != data->second.end())
600 {
James Feist3eb82622019-02-08 13:10:22 -0800601 limits.second = std::visit(VariantToDoubleVisitor(), maxFind->second);
James Feist87d713a2018-12-06 16:06:24 -0800602 }
James Feistfc94b212019-02-06 16:14:51 -0800603}
James Feist82bac4c2019-03-11 11:16:53 -0700604
605void createAssociation(
606 std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
607 const std::string& path)
608{
609 if (association)
610 {
Ed Tanous2e466962025-01-30 10:59:49 -0800611 std::filesystem::path p(path);
James Feistd2543f82019-04-09 11:10:06 -0700612
James Feist82bac4c2019-03-11 11:16:53 -0700613 std::vector<Association> associations;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700614 associations.emplace_back("chassis", "all_sensors",
615 p.parent_path().string());
James Feist2adc95c2019-09-30 14:55:28 -0700616 association->register_property("Associations", associations);
James Feist82bac4c2019-03-11 11:16:53 -0700617 association->initialize();
618 }
James Feist43d32fe2019-09-04 10:35:20 -0700619}
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800620
621void setInventoryAssociation(
Ed Tanous8a57ec02020-10-09 12:46:52 -0700622 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000623 const std::string& inventoryPath, const std::string& chassisPath)
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800624{
625 if (association)
626 {
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800627 std::vector<Association> associations;
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000628 associations.emplace_back("inventory", "sensors", inventoryPath);
629 associations.emplace_back("chassis", "all_sensors", chassisPath);
AppaRao Pulic82213c2020-02-27 01:24:58 +0530630
James Feist2adc95c2019-09-30 14:55:28 -0700631 association->register_property("Associations", associations);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800632 association->initialize();
633 }
634}
635
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000636std::optional<std::string> findContainingChassis(std::string_view configParent,
637 const GetSubTreeType& subtree)
638{
639 // A parent that is a chassis takes precedence
640 for (const auto& [obj, services] : subtree)
641 {
642 if (obj == configParent)
643 {
644 return obj;
645 }
646 }
647
648 // If the parent is not a chassis, the system chassis is used. This does not
649 // work if there is more than one System, but we assume there is only one
650 // today.
651 for (const auto& [obj, services] : subtree)
652 {
653 for (const auto& [service, interfaces] : services)
654 {
655 if (std::find(interfaces.begin(), interfaces.end(),
656 "xyz.openbmc_project.Inventory.Item.System") !=
657 interfaces.end())
658 {
659 return obj;
660 }
661 }
662 }
663 return std::nullopt;
664}
665
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800666void createInventoryAssoc(
Ed Tanous8a57ec02020-10-09 12:46:52 -0700667 const std::shared_ptr<sdbusplus::asio::connection>& conn,
668 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800669 const std::string& path)
670{
671 if (!association)
672 {
673 return;
674 }
AppaRao Pulic82213c2020-02-27 01:24:58 +0530675
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000676 constexpr auto allInterfaces = std::to_array({
677 "xyz.openbmc_project.Inventory.Item.Board",
678 "xyz.openbmc_project.Inventory.Item.Chassis",
679 });
680
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800681 conn->async_method_call(
682 [association, path](const boost::system::error_code ec,
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000683 const GetSubTreeType& subtree) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400684 // The parent of the config is always the inventory object, and may
685 // be the associated chassis. If the parent is not itself a chassis
686 // or board, the sensor is associated with the system chassis.
Ed Tanous2e466962025-01-30 10:59:49 -0800687 std::string parent =
688 std::filesystem::path(path).parent_path().string();
Patrick Williams2aaf7172024-08-16 15:20:40 -0400689 if (ec)
690 {
691 // In case of error, set the default associations and
692 // initialize the association Interface.
693 setInventoryAssociation(association, parent, parent);
694 return;
695 }
696 setInventoryAssociation(
697 association, parent,
698 findContainingChassis(parent, subtree).value_or(parent));
699 },
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000700 mapper::busName, mapper::path, mapper::interface, "GetSubTree",
701 "/xyz/openbmc_project/inventory/system", 2, allInterfaces);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800702}
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200703
704std::optional<double> readFile(const std::string& thresholdFile,
705 const double& scaleFactor)
706{
707 std::string line;
708 std::ifstream labelFile(thresholdFile);
709 if (labelFile.good())
710 {
711 std::getline(labelFile, line);
712 labelFile.close();
713
714 try
715 {
716 return std::stod(line) / scaleFactor;
717 }
718 catch (const std::invalid_argument&)
719 {
720 return std::nullopt;
721 }
722 }
723 return std::nullopt;
724}
725
Patrick Williams556e04b2025-02-01 08:22:22 -0500726std::optional<std::tuple<std::string, std::string, std::string>> splitFileName(
727 const std::filesystem::path& filePath)
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200728{
729 if (filePath.has_filename())
730 {
731 const auto fileName = filePath.filename().string();
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200732
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200733 size_t numberPos = std::strcspn(fileName.c_str(), "1234567890");
734 size_t itemPos = std::strcspn(fileName.c_str(), "_");
735
736 if (numberPos > 0 && itemPos > numberPos && fileName.size() > itemPos)
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200737 {
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200738 return std::make_optional(
739 std::make_tuple(fileName.substr(0, numberPos),
740 fileName.substr(numberPos, itemPos - numberPos),
741 fileName.substr(itemPos + 1, fileName.size())));
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200742 }
743 }
744 return std::nullopt;
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200745}
Bruce Lee1263c3d2021-06-04 15:16:33 +0800746
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530747static void handleSpecialModeChange(const std::string& manufacturingModeStatus)
748{
749 manufacturingMode = false;
750 if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security."
751 "SpecialMode.Modes.Manufacturing")
752 {
753 manufacturingMode = true;
754 }
Ed Tanous74cffa82022-01-25 13:00:28 -0800755 if (validateUnsecureFeature == 1)
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530756 {
757 if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security."
758 "SpecialMode.Modes.ValidationUnsecure")
759 {
760 manufacturingMode = true;
761 }
762 }
763}
764
Bruce Lee1263c3d2021-06-04 15:16:33 +0800765void setupManufacturingModeMatch(sdbusplus::asio::connection& conn)
766{
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530767 namespace rules = sdbusplus::bus::match::rules;
768 static constexpr const char* specialModeInterface =
769 "xyz.openbmc_project.Security.SpecialMode";
770
771 const std::string filterSpecialModeIntfAdd =
772 rules::interfacesAdded() +
773 rules::argNpath(0, "/xyz/openbmc_project/security/special_mode");
Patrick Williams92f8f512022-07-22 19:26:55 -0500774 static std::unique_ptr<sdbusplus::bus::match_t> specialModeIntfMatch =
Patrick Williams597e8422023-10-20 11:19:01 -0500775 std::make_unique<sdbusplus::bus::match_t>(
776 conn, filterSpecialModeIntfAdd, [](sdbusplus::message_t& m) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400777 sdbusplus::message::object_path path;
778 using PropertyMap =
779 boost::container::flat_map<std::string,
780 std::variant<std::string>>;
781 boost::container::flat_map<std::string, PropertyMap>
782 interfaceAdded;
783 m.read(path, interfaceAdded);
784 auto intfItr = interfaceAdded.find(specialModeInterface);
785 if (intfItr == interfaceAdded.end())
786 {
787 return;
788 }
789 PropertyMap& propertyList = intfItr->second;
790 auto itr = propertyList.find("SpecialMode");
791 if (itr == propertyList.end())
792 {
George Liu61984352025-02-24 14:47:34 +0800793 lg2::error("error getting SpecialMode property");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400794 return;
795 }
796 auto* manufacturingModeStatus =
797 std::get_if<std::string>(&itr->second);
798 handleSpecialModeChange(*manufacturingModeStatus);
799 });
Bruce Lee1263c3d2021-06-04 15:16:33 +0800800
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530801 const std::string filterSpecialModeChange =
802 rules::type::signal() + rules::member("PropertiesChanged") +
803 rules::interface("org.freedesktop.DBus.Properties") +
804 rules::argN(0, specialModeInterface);
Patrick Williams92f8f512022-07-22 19:26:55 -0500805 static std::unique_ptr<sdbusplus::bus::match_t> specialModeChangeMatch =
Patrick Williams2aaf7172024-08-16 15:20:40 -0400806 std::make_unique<sdbusplus::bus::match_t>(
807 conn, filterSpecialModeChange, [](sdbusplus::message_t& m) {
808 std::string interfaceName;
809 boost::container::flat_map<std::string,
810 std::variant<std::string>>
811 propertiesChanged;
Bruce Lee1263c3d2021-06-04 15:16:33 +0800812
Patrick Williams2aaf7172024-08-16 15:20:40 -0400813 m.read(interfaceName, propertiesChanged);
814 auto itr = propertiesChanged.find("SpecialMode");
815 if (itr == propertiesChanged.end())
816 {
817 return;
818 }
819 auto* manufacturingModeStatus =
820 std::get_if<std::string>(&itr->second);
821 handleSpecialModeChange(*manufacturingModeStatus);
822 });
Bruce Lee1263c3d2021-06-04 15:16:33 +0800823
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530824 conn.async_method_call(
825 [](const boost::system::error_code ec,
826 const std::variant<std::string>& getManufactMode) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400827 if (ec)
828 {
George Liu61984352025-02-24 14:47:34 +0800829 lg2::error(
830 "error getting SpecialMode status: '{ERROR_MESSAGE}'",
831 "ERROR_MESSAGE", ec.message());
Patrick Williams2aaf7172024-08-16 15:20:40 -0400832 return;
833 }
834 const auto* manufacturingModeStatus =
835 std::get_if<std::string>(&getManufactMode);
836 handleSpecialModeChange(*manufacturingModeStatus);
837 },
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530838 "xyz.openbmc_project.SpecialMode",
839 "/xyz/openbmc_project/security/special_mode",
840 "org.freedesktop.DBus.Properties", "Get", specialModeInterface,
841 "SpecialMode");
Bruce Lee1263c3d2021-06-04 15:16:33 +0800842}
843
844bool getManufacturingMode()
845{
846 return manufacturingMode;
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000847}
Zev Weiss214d9712022-08-12 12:54:31 -0700848
849std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
850 setupPropertiesChangedMatches(
851 sdbusplus::asio::connection& bus, std::span<const char* const> types,
852 const std::function<void(sdbusplus::message_t&)>& handler)
853{
854 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches;
855 for (const char* type : types)
856 {
857 auto match = std::make_unique<sdbusplus::bus::match_t>(
858 static_cast<sdbusplus::bus_t&>(bus),
859 "type='signal',member='PropertiesChanged',path_namespace='" +
Zev Weiss054aad82022-08-18 01:37:34 -0700860 std::string(inventoryPath) + "',arg0namespace='" +
861 configInterfaceName(type) + "'",
Zev Weiss214d9712022-08-12 12:54:31 -0700862 handler);
863 matches.emplace_back(std::move(match));
864 }
865 return matches;
866}
Zev Weissdabd48d2022-08-03 15:43:17 -0700867
868std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
869 setupPropertiesChangedMatches(
870 sdbusplus::asio::connection& bus, const I2CDeviceTypeMap& typeMap,
871 const std::function<void(sdbusplus::message_t&)>& handler)
872{
873 std::vector<const char*> types;
874 types.reserve(typeMap.size());
875 for (const auto& [type, dt] : typeMap)
876 {
877 types.push_back(type.data());
878 }
879 return setupPropertiesChangedMatches(bus, {types}, handler);
880}