blob: f109b4d435dbb8ffa95be4186a0209e634157014 [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
Andrew Jefferye98adf52023-10-11 23:23:18 +103028#include <charconv>
James Feist24f02f22019-04-15 11:05:39 -070029#include <filesystem>
James Feist6714a252018-09-10 15:26:18 -070030#include <fstream>
Patrick Venture96e97db2019-10-31 13:44:38 -070031#include <memory>
James Feist6714a252018-09-10 15:26:18 -070032#include <regex>
Patrick Venture96e97db2019-10-31 13:44:38 -070033#include <stdexcept>
34#include <string>
35#include <utility>
36#include <variant>
37#include <vector>
James Feist6714a252018-09-10 15:26:18 -070038
James Feistcf3bce62019-01-08 10:07:19 -080039namespace fs = std::filesystem;
James Feist6714a252018-09-10 15:26:18 -070040
James Feist6ef20402019-01-07 16:45:08 -080041static bool powerStatusOn = false;
James Feistfc94b212019-02-06 16:14:51 -080042static bool biosHasPost = false;
Bruce Lee1263c3d2021-06-04 15:16:33 +080043static bool manufacturingMode = false;
Thu Nguyen6db8aae2022-10-04 08:12:48 +070044static bool chassisStatusOn = false;
James Feist58295ad2019-05-30 15:01:41 -070045
Patrick Williams92f8f512022-07-22 19:26:55 -050046static std::unique_ptr<sdbusplus::bus::match_t> powerMatch = nullptr;
47static std::unique_ptr<sdbusplus::bus::match_t> postMatch = nullptr;
Thu Nguyen6db8aae2022-10-04 08:12:48 +070048static std::unique_ptr<sdbusplus::bus::match_t> chassisMatch = nullptr;
James Feist71d31b22019-01-02 16:57:54 -080049
Jason Ling100c20b2020-08-11 14:50:33 -070050/**
51 * return the contents of a file
52 * @param[in] hwmonFile - the path to the file to read
53 * @return the contents of the file as a string or nullopt if the file could not
54 * be opened.
55 */
56
57std::optional<std::string> openAndRead(const std::string& hwmonFile)
58{
59 std::string fileVal;
60 std::ifstream fileStream(hwmonFile);
61 if (!fileStream.is_open())
62 {
63 return std::nullopt;
64 }
65 std::getline(fileStream, fileVal);
66 return fileVal;
67}
68
69/**
70 * given a hwmon temperature base name if valid return the full path else
71 * nullopt
72 * @param[in] directory - the hwmon sysfs directory
73 * @param[in] permitSet - a set of labels or hwmon basenames to permit. If this
74 * is empty then *everything* is permitted.
75 * @return a string to the full path of the file to create a temp sensor with or
76 * nullopt to indicate that no sensor should be created for this basename.
77 */
78std::optional<std::string>
79 getFullHwmonFilePath(const std::string& directory,
80 const std::string& hwmonBaseName,
81 const std::set<std::string>& permitSet)
82{
83 std::optional<std::string> result;
84 std::string filename;
85 if (permitSet.empty())
86 {
87 result = directory + "/" + hwmonBaseName + "_input";
88 return result;
89 }
90 filename = directory + "/" + hwmonBaseName + "_label";
91 auto searchVal = openAndRead(filename);
92 if (!searchVal)
93 {
94 /* if the hwmon temp doesn't have a corresponding label file
95 * then use the hwmon temperature base name
96 */
97 searchVal = hwmonBaseName;
98 }
99 if (permitSet.find(*searchVal) != permitSet.end())
100 {
101 result = directory + "/" + hwmonBaseName + "_input";
102 }
103 return result;
104}
105
106/**
107 * retrieve a set of basenames and labels to allow sensor creation for.
108 * @param[in] config - a map representing the configuration for a specific
109 * device
110 * @return a set of basenames and labels to allow sensor creation for. An empty
111 * set indicates that everything is permitted.
112 */
113std::set<std::string> getPermitSet(const SensorBaseConfigMap& config)
114{
115 auto permitAttribute = config.find("Labels");
116 std::set<std::string> permitSet;
117 if (permitAttribute != config.end())
118 {
119 try
120 {
121 auto val =
122 std::get<std::vector<std::string>>(permitAttribute->second);
123
124 permitSet.insert(std::make_move_iterator(val.begin()),
125 std::make_move_iterator(val.end()));
126 }
127 catch (const std::bad_variant_access& err)
128 {
129 std::cerr << err.what()
130 << ":PermitList does not contain a list, wrong "
131 "variant type.\n";
132 }
133 }
134 return permitSet;
135}
136
James Feist6714a252018-09-10 15:26:18 -0700137bool getSensorConfiguration(
138 const std::string& type,
139 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
140 ManagedObjectType& resp, bool useCache)
141{
142 static ManagedObjectType managedObj;
Zev Weiss054aad82022-08-18 01:37:34 -0700143 std::string typeIntf = configInterfaceName(type);
James Feist6714a252018-09-10 15:26:18 -0700144
145 if (!useCache)
146 {
147 managedObj.clear();
Patrick Williams92f8f512022-07-22 19:26:55 -0500148 sdbusplus::message_t getManagedObjects =
James Feist6714a252018-09-10 15:26:18 -0700149 dbusConnection->new_method_call(
JeffLin2c5a1f22022-10-05 15:19:09 +0800150 entityManagerName, "/xyz/openbmc_project/inventory",
151 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
James Feist6714a252018-09-10 15:26:18 -0700152 try
153 {
Patrick Williams92f8f512022-07-22 19:26:55 -0500154 sdbusplus::message_t reply =
James Feist6714a252018-09-10 15:26:18 -0700155 dbusConnection->call(getManagedObjects);
Yoo, Jae Hyun0e022052018-10-15 14:05:37 -0700156 reply.read(managedObj);
James Feist6714a252018-09-10 15:26:18 -0700157 }
Patrick Williams92f8f512022-07-22 19:26:55 -0500158 catch (const sdbusplus::exception_t& e)
James Feist6714a252018-09-10 15:26:18 -0700159 {
Jason Ling5747fab2019-10-02 16:46:23 -0700160 std::cerr << "While calling GetManagedObjects on service:"
161 << entityManagerName << " exception name:" << e.name()
162 << "and description:" << e.description()
163 << " was thrown\n";
James Feist6714a252018-09-10 15:26:18 -0700164 return false;
165 }
166 }
167 for (const auto& pathPair : managedObj)
168 {
Zev Weissfd3d5f72022-08-12 18:21:02 -0700169 for (const auto& [intf, cfg] : pathPair.second)
James Feist6714a252018-09-10 15:26:18 -0700170 {
Zev Weiss054aad82022-08-18 01:37:34 -0700171 if (intf.starts_with(typeIntf))
James Feist6714a252018-09-10 15:26:18 -0700172 {
Zev Weiss79d8aef2022-08-17 21:45:44 -0700173 resp.emplace(pathPair);
James Feist6714a252018-09-10 15:26:18 -0700174 break;
175 }
176 }
James Feist6714a252018-09-10 15:26:18 -0700177 }
178 return true;
179}
180
Lei YU6a4e9732021-10-20 13:27:34 +0800181bool findFiles(const fs::path& dirPath, std::string_view matchString,
Ed Tanous8a57ec02020-10-09 12:46:52 -0700182 std::vector<fs::path>& foundPaths, int symlinkDepth)
James Feist6714a252018-09-10 15:26:18 -0700183{
Lei YU6a4e9732021-10-20 13:27:34 +0800184 std::error_code ec;
185 if (!fs::exists(dirPath, ec))
Ed Tanous8a57ec02020-10-09 12:46:52 -0700186 {
James Feist6714a252018-09-10 15:26:18 -0700187 return false;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700188 }
James Feist6714a252018-09-10 15:26:18 -0700189
Lei YU6a4e9732021-10-20 13:27:34 +0800190 std::vector<std::regex> matchPieces;
191
192 size_t pos = 0;
193 std::string token;
194 // Generate the regex expressions list from the match we were given
195 while ((pos = matchString.find('/')) != std::string::npos)
196 {
197 token = matchString.substr(0, pos);
198 matchPieces.emplace_back(token);
199 matchString.remove_prefix(pos + 1);
200 }
201 matchPieces.emplace_back(std::string{matchString});
202
203 // Check if the match string contains directories, and skip the match of
204 // subdirectory if not
205 if (matchPieces.size() <= 1)
206 {
207 std::regex search(std::string{matchString});
208 std::smatch match;
209 for (auto p = fs::recursive_directory_iterator(
210 dirPath, fs::directory_options::follow_directory_symlink);
211 p != fs::recursive_directory_iterator(); ++p)
212 {
213 std::string path = p->path().string();
214 if (!is_directory(*p))
215 {
216 if (std::regex_search(path, match, search))
217 {
218 foundPaths.emplace_back(p->path());
219 }
220 }
221 if (p.depth() >= symlinkDepth)
222 {
223 p.disable_recursion_pending();
224 }
225 }
226 return true;
227 }
228
229 // The match string contains directories, verify each level of sub
230 // directories
Ed Tanous8a57ec02020-10-09 12:46:52 -0700231 for (auto p = fs::recursive_directory_iterator(
232 dirPath, fs::directory_options::follow_directory_symlink);
233 p != fs::recursive_directory_iterator(); ++p)
James Feist6714a252018-09-10 15:26:18 -0700234 {
Lei YU6a4e9732021-10-20 13:27:34 +0800235 std::vector<std::regex>::iterator matchPiece = matchPieces.begin();
236 fs::path::iterator pathIt = p->path().begin();
237 for (const fs::path& dir : dirPath)
238 {
239 if (dir.empty())
240 {
241 // When the path ends with '/', it gets am empty path
242 // skip such case.
243 break;
244 }
245 pathIt++;
246 }
247
248 while (pathIt != p->path().end())
249 {
250 // Found a path deeper than match.
251 if (matchPiece == matchPieces.end())
252 {
253 p.disable_recursion_pending();
254 break;
255 }
256 std::smatch match;
257 std::string component = pathIt->string();
258 std::regex regexPiece(*matchPiece);
259 if (!std::regex_match(component, match, regexPiece))
260 {
261 // path prefix doesn't match, no need to iterate further
262 p.disable_recursion_pending();
263 break;
264 }
265 matchPiece++;
266 pathIt++;
267 }
268
Ed Tanous8a57ec02020-10-09 12:46:52 -0700269 if (!is_directory(*p))
James Feist6714a252018-09-10 15:26:18 -0700270 {
Lei YU6a4e9732021-10-20 13:27:34 +0800271 if (matchPiece == matchPieces.end())
Ed Tanous8a57ec02020-10-09 12:46:52 -0700272 {
273 foundPaths.emplace_back(p->path());
274 }
James Feist6714a252018-09-10 15:26:18 -0700275 }
Lei YU6a4e9732021-10-20 13:27:34 +0800276
Ed Tanous8a57ec02020-10-09 12:46:52 -0700277 if (p.depth() >= symlinkDepth)
James Feist6714a252018-09-10 15:26:18 -0700278 {
Ed Tanous8a57ec02020-10-09 12:46:52 -0700279 p.disable_recursion_pending();
James Feist6714a252018-09-10 15:26:18 -0700280 }
281 }
282 return true;
283}
284
James Feist71d31b22019-01-02 16:57:54 -0800285bool isPowerOn(void)
James Feist6714a252018-09-10 15:26:18 -0700286{
James Feist71d31b22019-01-02 16:57:54 -0800287 if (!powerMatch)
James Feist6714a252018-09-10 15:26:18 -0700288 {
James Feist71d31b22019-01-02 16:57:54 -0800289 throw std::runtime_error("Power Match Not Created");
James Feist6714a252018-09-10 15:26:18 -0700290 }
James Feist71d31b22019-01-02 16:57:54 -0800291 return powerStatusOn;
292}
293
James Feistfc94b212019-02-06 16:14:51 -0800294bool hasBiosPost(void)
295{
James Feist52497fd2019-06-07 13:01:33 -0700296 if (!postMatch)
James Feistfc94b212019-02-06 16:14:51 -0800297 {
James Feist52497fd2019-06-07 13:01:33 -0700298 throw std::runtime_error("Post Match Not Created");
James Feistfc94b212019-02-06 16:14:51 -0800299 }
300 return biosHasPost;
301}
302
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700303bool isChassisOn(void)
304{
305 if (!chassisMatch)
306 {
307 throw std::runtime_error("Chassis On Match Not Created");
308 }
309 return chassisStatusOn;
310}
311
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000312bool readingStateGood(const PowerState& powerState)
313{
314 if (powerState == PowerState::on && !isPowerOn())
315 {
316 return false;
317 }
318 if (powerState == PowerState::biosPost && (!hasBiosPost() || !isPowerOn()))
319 {
320 return false;
321 }
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700322 if (powerState == PowerState::chassisOn && !isChassisOn())
323 {
324 return false;
325 }
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000326
327 return true;
328}
329
James Feist8aeffd92020-09-14 12:08:28 -0700330static void
331 getPowerStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
332 size_t retries = 2)
333{
334 conn->async_method_call(
335 [conn, retries](boost::system::error_code ec,
336 const std::variant<std::string>& state) {
Ed Tanousbb679322022-05-16 16:10:00 -0700337 if (ec)
338 {
Ed Tanous2049bd22022-07-09 07:20:26 -0700339 if (retries != 0U)
James Feist8aeffd92020-09-14 12:08:28 -0700340 {
Ed Tanousbb679322022-05-16 16:10:00 -0700341 auto timer = std::make_shared<boost::asio::steady_timer>(
342 conn->get_io_context());
343 timer->expires_after(std::chrono::seconds(15));
344 timer->async_wait(
345 [timer, conn, retries](boost::system::error_code) {
346 getPowerStatus(conn, retries - 1);
347 });
James Feist8aeffd92020-09-14 12:08:28 -0700348 return;
349 }
Ed Tanousbb679322022-05-16 16:10:00 -0700350
351 // we commonly come up before power control, we'll capture the
352 // property change later
353 std::cerr << "error getting power status " << ec.message() << "\n";
354 return;
355 }
Zev Weiss6c106d62022-08-17 20:50:00 -0700356 powerStatusOn = std::get<std::string>(state).ends_with(".Running");
Patrick Williams597e8422023-10-20 11:19:01 -0500357 },
James Feist8aeffd92020-09-14 12:08:28 -0700358 power::busname, power::path, properties::interface, properties::get,
359 power::interface, power::property);
360}
361
362static void
363 getPostStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
364 size_t retries = 2)
365{
366 conn->async_method_call(
367 [conn, retries](boost::system::error_code ec,
368 const std::variant<std::string>& state) {
Ed Tanousbb679322022-05-16 16:10:00 -0700369 if (ec)
370 {
Ed Tanous2049bd22022-07-09 07:20:26 -0700371 if (retries != 0U)
James Feist8aeffd92020-09-14 12:08:28 -0700372 {
Ed Tanousbb679322022-05-16 16:10:00 -0700373 auto timer = std::make_shared<boost::asio::steady_timer>(
374 conn->get_io_context());
375 timer->expires_after(std::chrono::seconds(15));
376 timer->async_wait(
377 [timer, conn, retries](boost::system::error_code) {
378 getPostStatus(conn, retries - 1);
379 });
James Feist8aeffd92020-09-14 12:08:28 -0700380 return;
381 }
Ed Tanousbb679322022-05-16 16:10:00 -0700382 // we commonly come up before power control, we'll capture the
383 // property change later
384 std::cerr << "error getting post status " << ec.message() << "\n";
385 return;
386 }
Ed Tanous2049bd22022-07-09 07:20:26 -0700387 const auto& value = std::get<std::string>(state);
Ed Tanousbb679322022-05-16 16:10:00 -0700388 biosHasPost = (value != "Inactive") &&
389 (value != "xyz.openbmc_project.State.OperatingSystem."
390 "Status.OSStatus.Inactive");
Patrick Williams597e8422023-10-20 11:19:01 -0500391 },
James Feist8aeffd92020-09-14 12:08:28 -0700392 post::busname, post::path, properties::interface, properties::get,
393 post::interface, post::property);
394}
395
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700396static void
397 getChassisStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
398 size_t retries = 2)
399{
400 conn->async_method_call(
401 [conn, retries](boost::system::error_code ec,
402 const std::variant<std::string>& state) {
403 if (ec)
404 {
405 if (retries != 0U)
406 {
407 auto timer = std::make_shared<boost::asio::steady_timer>(
408 conn->get_io_context());
409 timer->expires_after(std::chrono::seconds(15));
410 timer->async_wait(
411 [timer, conn, retries](boost::system::error_code) {
412 getChassisStatus(conn, retries - 1);
413 });
414 return;
415 }
416
417 // we commonly come up before power control, we'll capture the
418 // property change later
419 std::cerr << "error getting chassis power status " << ec.message()
420 << "\n";
421 return;
422 }
423 chassisStatusOn = std::get<std::string>(state).ends_with(chassis::sOn);
Patrick Williams597e8422023-10-20 11:19:01 -0500424 },
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700425 chassis::busname, chassis::path, properties::interface, properties::get,
426 chassis::interface, chassis::property);
427}
428
Zev Weiss88cb29d2022-05-09 03:46:15 +0000429void setupPowerMatchCallback(
430 const std::shared_ptr<sdbusplus::asio::connection>& conn,
431 std::function<void(PowerState type, bool state)>&& hostStatusCallback)
James Feist71d31b22019-01-02 16:57:54 -0800432{
James Feist43d32fe2019-09-04 10:35:20 -0700433 static boost::asio::steady_timer timer(conn->get_io_context());
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700434 static boost::asio::steady_timer timerChassisOn(conn->get_io_context());
James Feist6714a252018-09-10 15:26:18 -0700435 // create a match for powergood changes, first time do a method call to
James Feist71d31b22019-01-02 16:57:54 -0800436 // cache the correct value
James Feist52497fd2019-06-07 13:01:33 -0700437 if (powerMatch)
438 {
439 return;
440 }
James Feist6714a252018-09-10 15:26:18 -0700441
Patrick Williams92f8f512022-07-22 19:26:55 -0500442 powerMatch = std::make_unique<sdbusplus::bus::match_t>(
443 static_cast<sdbusplus::bus_t&>(*conn),
James Feist52497fd2019-06-07 13:01:33 -0700444 "type='signal',interface='" + std::string(properties::interface) +
445 "',path='" + std::string(power::path) + "',arg0='" +
446 std::string(power::interface) + "'",
Zev Weiss88cb29d2022-05-09 03:46:15 +0000447 [hostStatusCallback](sdbusplus::message_t& message) {
Ed Tanousbb679322022-05-16 16:10:00 -0700448 std::string objectName;
449 boost::container::flat_map<std::string, std::variant<std::string>>
450 values;
451 message.read(objectName, values);
452 auto findState = values.find(power::property);
453 if (findState != values.end())
454 {
Zev Weiss6c106d62022-08-17 20:50:00 -0700455 bool on =
456 std::get<std::string>(findState->second).ends_with(".Running");
Ed Tanousbb679322022-05-16 16:10:00 -0700457 if (!on)
James Feist6714a252018-09-10 15:26:18 -0700458 {
Ed Tanousbb679322022-05-16 16:10:00 -0700459 timer.cancel();
460 powerStatusOn = false;
Zev Weiss88cb29d2022-05-09 03:46:15 +0000461 hostStatusCallback(PowerState::on, powerStatusOn);
Ed Tanousbb679322022-05-16 16:10:00 -0700462 return;
463 }
464 // on comes too quickly
465 timer.expires_after(std::chrono::seconds(10));
Zev Weiss88cb29d2022-05-09 03:46:15 +0000466 timer.async_wait(
467 [hostStatusCallback](boost::system::error_code ec) {
Ed Tanousbb679322022-05-16 16:10:00 -0700468 if (ec == boost::asio::error::operation_aborted)
James Feist43d32fe2019-09-04 10:35:20 -0700469 {
James Feist43d32fe2019-09-04 10:35:20 -0700470 return;
471 }
Ed Tanousbb679322022-05-16 16:10:00 -0700472 if (ec)
473 {
474 std::cerr << "Timer error " << ec.message() << "\n";
475 return;
476 }
477 powerStatusOn = true;
Zev Weiss88cb29d2022-05-09 03:46:15 +0000478 hostStatusCallback(PowerState::on, powerStatusOn);
Ed Tanousbb679322022-05-16 16:10:00 -0700479 });
480 }
Patrick Williams597e8422023-10-20 11:19:01 -0500481 });
James Feist52497fd2019-06-07 13:01:33 -0700482
Patrick Williams92f8f512022-07-22 19:26:55 -0500483 postMatch = std::make_unique<sdbusplus::bus::match_t>(
484 static_cast<sdbusplus::bus_t&>(*conn),
James Feist52497fd2019-06-07 13:01:33 -0700485 "type='signal',interface='" + std::string(properties::interface) +
486 "',path='" + std::string(post::path) + "',arg0='" +
487 std::string(post::interface) + "'",
Zev Weiss88cb29d2022-05-09 03:46:15 +0000488 [hostStatusCallback](sdbusplus::message_t& message) {
Ed Tanousbb679322022-05-16 16:10:00 -0700489 std::string objectName;
490 boost::container::flat_map<std::string, std::variant<std::string>>
491 values;
492 message.read(objectName, values);
493 auto findState = values.find(post::property);
494 if (findState != values.end())
495 {
496 auto& value = std::get<std::string>(findState->second);
497 biosHasPost = (value != "Inactive") &&
498 (value != "xyz.openbmc_project.State.OperatingSystem."
499 "Status.OSStatus.Inactive");
Zev Weiss88cb29d2022-05-09 03:46:15 +0000500 hostStatusCallback(PowerState::biosPost, biosHasPost);
Ed Tanousbb679322022-05-16 16:10:00 -0700501 }
Patrick Williams597e8422023-10-20 11:19:01 -0500502 });
James Feistfc94b212019-02-06 16:14:51 -0800503
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700504 chassisMatch = std::make_unique<sdbusplus::bus::match_t>(
505 static_cast<sdbusplus::bus_t&>(*conn),
506 "type='signal',interface='" + std::string(properties::interface) +
507 "',path='" + std::string(chassis::path) + "',arg0='" +
508 std::string(chassis::interface) + "'",
509 [hostStatusCallback](sdbusplus::message_t& message) {
510 std::string objectName;
511 boost::container::flat_map<std::string, std::variant<std::string>>
512 values;
513 message.read(objectName, values);
514 auto findState = values.find(chassis::property);
515 if (findState != values.end())
516 {
517 bool on = std::get<std::string>(findState->second)
518 .ends_with(chassis::sOn);
519 if (!on)
520 {
521 timerChassisOn.cancel();
522 chassisStatusOn = false;
523 hostStatusCallback(PowerState::chassisOn, chassisStatusOn);
524 return;
525 }
526 // on comes too quickly
527 timerChassisOn.expires_after(std::chrono::seconds(10));
528 timerChassisOn.async_wait(
529 [hostStatusCallback](boost::system::error_code ec) {
530 if (ec == boost::asio::error::operation_aborted)
531 {
532 return;
533 }
534 if (ec)
535 {
536 std::cerr << "Timer error " << ec.message() << "\n";
537 return;
538 }
539 chassisStatusOn = true;
540 hostStatusCallback(PowerState::chassisOn, chassisStatusOn);
541 });
542 }
Patrick Williams597e8422023-10-20 11:19:01 -0500543 });
James Feist8aeffd92020-09-14 12:08:28 -0700544 getPowerStatus(conn);
545 getPostStatus(conn);
Thu Nguyen6db8aae2022-10-04 08:12:48 +0700546 getChassisStatus(conn);
James Feist3f0e8762018-11-27 11:30:42 -0800547}
James Feist87d713a2018-12-06 16:06:24 -0800548
Zev Weiss88cb29d2022-05-09 03:46:15 +0000549void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
550{
551 setupPowerMatchCallback(conn, [](PowerState, bool) {});
552}
553
James Feist87d713a2018-12-06 16:06:24 -0800554// replaces limits if MinReading and MaxReading are found.
555void findLimits(std::pair<double, double>& limits,
556 const SensorBaseConfiguration* data)
557{
Ed Tanous2049bd22022-07-09 07:20:26 -0700558 if (data == nullptr)
James Feist87d713a2018-12-06 16:06:24 -0800559 {
560 return;
561 }
562 auto maxFind = data->second.find("MaxReading");
563 auto minFind = data->second.find("MinReading");
564
565 if (minFind != data->second.end())
566 {
James Feist3eb82622019-02-08 13:10:22 -0800567 limits.first = std::visit(VariantToDoubleVisitor(), minFind->second);
James Feist87d713a2018-12-06 16:06:24 -0800568 }
569 if (maxFind != data->second.end())
570 {
James Feist3eb82622019-02-08 13:10:22 -0800571 limits.second = std::visit(VariantToDoubleVisitor(), maxFind->second);
James Feist87d713a2018-12-06 16:06:24 -0800572 }
James Feistfc94b212019-02-06 16:14:51 -0800573}
James Feist82bac4c2019-03-11 11:16:53 -0700574
575void createAssociation(
576 std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
577 const std::string& path)
578{
579 if (association)
580 {
Lei YU6a4e9732021-10-20 13:27:34 +0800581 fs::path p(path);
James Feistd2543f82019-04-09 11:10:06 -0700582
James Feist82bac4c2019-03-11 11:16:53 -0700583 std::vector<Association> associations;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700584 associations.emplace_back("chassis", "all_sensors",
585 p.parent_path().string());
James Feist2adc95c2019-09-30 14:55:28 -0700586 association->register_property("Associations", associations);
James Feist82bac4c2019-03-11 11:16:53 -0700587 association->initialize();
588 }
James Feist43d32fe2019-09-04 10:35:20 -0700589}
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800590
591void setInventoryAssociation(
Ed Tanous8a57ec02020-10-09 12:46:52 -0700592 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000593 const std::string& inventoryPath, const std::string& chassisPath)
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800594{
595 if (association)
596 {
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800597 std::vector<Association> associations;
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000598 associations.emplace_back("inventory", "sensors", inventoryPath);
599 associations.emplace_back("chassis", "all_sensors", chassisPath);
AppaRao Pulic82213c2020-02-27 01:24:58 +0530600
James Feist2adc95c2019-09-30 14:55:28 -0700601 association->register_property("Associations", associations);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800602 association->initialize();
603 }
604}
605
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000606std::optional<std::string> findContainingChassis(std::string_view configParent,
607 const GetSubTreeType& subtree)
608{
609 // A parent that is a chassis takes precedence
610 for (const auto& [obj, services] : subtree)
611 {
612 if (obj == configParent)
613 {
614 return obj;
615 }
616 }
617
618 // If the parent is not a chassis, the system chassis is used. This does not
619 // work if there is more than one System, but we assume there is only one
620 // today.
621 for (const auto& [obj, services] : subtree)
622 {
623 for (const auto& [service, interfaces] : services)
624 {
625 if (std::find(interfaces.begin(), interfaces.end(),
626 "xyz.openbmc_project.Inventory.Item.System") !=
627 interfaces.end())
628 {
629 return obj;
630 }
631 }
632 }
633 return std::nullopt;
634}
635
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800636void createInventoryAssoc(
Ed Tanous8a57ec02020-10-09 12:46:52 -0700637 const std::shared_ptr<sdbusplus::asio::connection>& conn,
638 const std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800639 const std::string& path)
640{
641 if (!association)
642 {
643 return;
644 }
AppaRao Pulic82213c2020-02-27 01:24:58 +0530645
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000646 constexpr auto allInterfaces = std::to_array({
647 "xyz.openbmc_project.Inventory.Item.Board",
648 "xyz.openbmc_project.Inventory.Item.Chassis",
649 });
650
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800651 conn->async_method_call(
652 [association, path](const boost::system::error_code ec,
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000653 const GetSubTreeType& subtree) {
654 // The parent of the config is always the inventory object, and may be
655 // the associated chassis. If the parent is not itself a chassis or
656 // board, the sensor is associated with the system chassis.
657 std::string parent = fs::path(path).parent_path().string();
Ed Tanousbb679322022-05-16 16:10:00 -0700658 if (ec)
659 {
660 // In case of error, set the default associations and
661 // initialize the association Interface.
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000662 setInventoryAssociation(association, parent, parent);
Ed Tanousbb679322022-05-16 16:10:00 -0700663 return;
664 }
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000665 setInventoryAssociation(
666 association, parent,
667 findContainingChassis(parent, subtree).value_or(parent));
Patrick Williams597e8422023-10-20 11:19:01 -0500668 },
Shounak Mitra8a28bf12022-10-17 18:07:50 +0000669 mapper::busName, mapper::path, mapper::interface, "GetSubTree",
670 "/xyz/openbmc_project/inventory/system", 2, allInterfaces);
Cheng C Yang5580f2f2019-09-19 09:01:47 +0800671}
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200672
673std::optional<double> readFile(const std::string& thresholdFile,
674 const double& scaleFactor)
675{
676 std::string line;
677 std::ifstream labelFile(thresholdFile);
678 if (labelFile.good())
679 {
680 std::getline(labelFile, line);
681 labelFile.close();
682
683 try
684 {
685 return std::stod(line) / scaleFactor;
686 }
687 catch (const std::invalid_argument&)
688 {
689 return std::nullopt;
690 }
691 }
692 return std::nullopt;
693}
694
695std::optional<std::tuple<std::string, std::string, std::string>>
Lei YU6a4e9732021-10-20 13:27:34 +0800696 splitFileName(const fs::path& filePath)
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200697{
698 if (filePath.has_filename())
699 {
700 const auto fileName = filePath.filename().string();
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200701
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200702 size_t numberPos = std::strcspn(fileName.c_str(), "1234567890");
703 size_t itemPos = std::strcspn(fileName.c_str(), "_");
704
705 if (numberPos > 0 && itemPos > numberPos && fileName.size() > itemPos)
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200706 {
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200707 return std::make_optional(
708 std::make_tuple(fileName.substr(0, numberPos),
709 fileName.substr(numberPos, itemPos - numberPos),
710 fileName.substr(itemPos + 1, fileName.size())));
Zbigniew Kurzynski63f38662020-06-09 13:02:11 +0200711 }
712 }
713 return std::nullopt;
Zbigniew Kurzynskidd648002020-07-10 16:44:16 +0200714}
Bruce Lee1263c3d2021-06-04 15:16:33 +0800715
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530716static void handleSpecialModeChange(const std::string& manufacturingModeStatus)
717{
718 manufacturingMode = false;
719 if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security."
720 "SpecialMode.Modes.Manufacturing")
721 {
722 manufacturingMode = true;
723 }
Ed Tanous74cffa82022-01-25 13:00:28 -0800724 if (validateUnsecureFeature == 1)
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530725 {
726 if (manufacturingModeStatus == "xyz.openbmc_project.Control.Security."
727 "SpecialMode.Modes.ValidationUnsecure")
728 {
729 manufacturingMode = true;
730 }
731 }
732}
733
Bruce Lee1263c3d2021-06-04 15:16:33 +0800734void setupManufacturingModeMatch(sdbusplus::asio::connection& conn)
735{
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530736 namespace rules = sdbusplus::bus::match::rules;
737 static constexpr const char* specialModeInterface =
738 "xyz.openbmc_project.Security.SpecialMode";
739
740 const std::string filterSpecialModeIntfAdd =
741 rules::interfacesAdded() +
742 rules::argNpath(0, "/xyz/openbmc_project/security/special_mode");
Patrick Williams92f8f512022-07-22 19:26:55 -0500743 static std::unique_ptr<sdbusplus::bus::match_t> specialModeIntfMatch =
Patrick Williams597e8422023-10-20 11:19:01 -0500744 std::make_unique<sdbusplus::bus::match_t>(
745 conn, filterSpecialModeIntfAdd, [](sdbusplus::message_t& m) {
Ed Tanousbb679322022-05-16 16:10:00 -0700746 sdbusplus::message::object_path path;
747 using PropertyMap =
748 boost::container::flat_map<std::string, std::variant<std::string>>;
749 boost::container::flat_map<std::string, PropertyMap> interfaceAdded;
750 m.read(path, interfaceAdded);
751 auto intfItr = interfaceAdded.find(specialModeInterface);
752 if (intfItr == interfaceAdded.end())
753 {
754 return;
755 }
756 PropertyMap& propertyList = intfItr->second;
757 auto itr = propertyList.find("SpecialMode");
758 if (itr == propertyList.end())
759 {
760 std::cerr << "error getting SpecialMode property "
761 << "\n";
762 return;
763 }
Ed Tanous2049bd22022-07-09 07:20:26 -0700764 auto* manufacturingModeStatus = std::get_if<std::string>(&itr->second);
Ed Tanousbb679322022-05-16 16:10:00 -0700765 handleSpecialModeChange(*manufacturingModeStatus);
Patrick Williams597e8422023-10-20 11:19:01 -0500766 });
Bruce Lee1263c3d2021-06-04 15:16:33 +0800767
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530768 const std::string filterSpecialModeChange =
769 rules::type::signal() + rules::member("PropertiesChanged") +
770 rules::interface("org.freedesktop.DBus.Properties") +
771 rules::argN(0, specialModeInterface);
Patrick Williams92f8f512022-07-22 19:26:55 -0500772 static std::unique_ptr<sdbusplus::bus::match_t> specialModeChangeMatch =
773 std::make_unique<sdbusplus::bus::match_t>(conn, filterSpecialModeChange,
774 [](sdbusplus::message_t& m) {
Ed Tanousbb679322022-05-16 16:10:00 -0700775 std::string interfaceName;
776 boost::container::flat_map<std::string, std::variant<std::string>>
777 propertiesChanged;
Bruce Lee1263c3d2021-06-04 15:16:33 +0800778
Ed Tanousbb679322022-05-16 16:10:00 -0700779 m.read(interfaceName, propertiesChanged);
780 auto itr = propertiesChanged.find("SpecialMode");
781 if (itr == propertiesChanged.end())
782 {
783 return;
784 }
Ed Tanous2049bd22022-07-09 07:20:26 -0700785 auto* manufacturingModeStatus = std::get_if<std::string>(&itr->second);
Ed Tanousbb679322022-05-16 16:10:00 -0700786 handleSpecialModeChange(*manufacturingModeStatus);
Patrick Williams597e8422023-10-20 11:19:01 -0500787 });
Bruce Lee1263c3d2021-06-04 15:16:33 +0800788
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530789 conn.async_method_call(
790 [](const boost::system::error_code ec,
791 const std::variant<std::string>& getManufactMode) {
Ed Tanousbb679322022-05-16 16:10:00 -0700792 if (ec)
793 {
794 std::cerr << "error getting SpecialMode status " << ec.message()
795 << "\n";
796 return;
797 }
Ed Tanous2049bd22022-07-09 07:20:26 -0700798 const auto* manufacturingModeStatus =
Ed Tanousbb679322022-05-16 16:10:00 -0700799 std::get_if<std::string>(&getManufactMode);
800 handleSpecialModeChange(*manufacturingModeStatus);
Patrick Williams597e8422023-10-20 11:19:01 -0500801 },
Arun P. Mohanan45f844a2021-11-03 22:18:09 +0530802 "xyz.openbmc_project.SpecialMode",
803 "/xyz/openbmc_project/security/special_mode",
804 "org.freedesktop.DBus.Properties", "Get", specialModeInterface,
805 "SpecialMode");
Bruce Lee1263c3d2021-06-04 15:16:33 +0800806}
807
808bool getManufacturingMode()
809{
810 return manufacturingMode;
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000811}
Zev Weiss214d9712022-08-12 12:54:31 -0700812
813std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
814 setupPropertiesChangedMatches(
815 sdbusplus::asio::connection& bus, std::span<const char* const> types,
816 const std::function<void(sdbusplus::message_t&)>& handler)
817{
818 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches;
819 for (const char* type : types)
820 {
821 auto match = std::make_unique<sdbusplus::bus::match_t>(
822 static_cast<sdbusplus::bus_t&>(bus),
823 "type='signal',member='PropertiesChanged',path_namespace='" +
Zev Weiss054aad82022-08-18 01:37:34 -0700824 std::string(inventoryPath) + "',arg0namespace='" +
825 configInterfaceName(type) + "'",
Zev Weiss214d9712022-08-12 12:54:31 -0700826 handler);
827 matches.emplace_back(std::move(match));
828 }
829 return matches;
830}
Zev Weissdabd48d2022-08-03 15:43:17 -0700831
832std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
833 setupPropertiesChangedMatches(
834 sdbusplus::asio::connection& bus, const I2CDeviceTypeMap& typeMap,
835 const std::function<void(sdbusplus::message_t&)>& handler)
836{
837 std::vector<const char*> types;
838 types.reserve(typeMap.size());
839 for (const auto& [type, dt] : typeMap)
840 {
841 types.push_back(type.data());
842 }
843 return setupPropertiesChangedMatches(bus, {types}, handler);
844}