blob: 83240beb0af4240314977c80320346fe54fdd50c [file] [log] [blame]
James Feist3cb5fec2018-01-23 14:41:51 -08001/*
2// Copyright (c) 2018 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*/
Brad Bishope45d8c72022-05-25 15:12:53 -040016/// \file entity_manager.cpp
James Feist3cb5fec2018-01-23 14:41:51 -080017
Brad Bishope45d8c72022-05-25 15:12:53 -040018#include "entity_manager.hpp"
James Feist1df06a42019-04-11 14:23:04 -070019
Christopher Meisfc9e7fd2025-04-03 13:13:35 +020020#include "../utils.hpp"
21#include "../variant_visitors.hpp"
Christopher Meisbdaa6b22025-04-02 10:49:02 +020022#include "configuration.hpp"
Christopher Meis12bea9b2025-04-03 10:14:42 +020023#include "dbus_interface.hpp"
Alexander Hansenf57a2592025-06-27 15:07:07 +020024#include "log_device_inventory.hpp"
Brad Bishope45d8c72022-05-25 15:12:53 -040025#include "overlay.hpp"
Christopher Meis26fbbd52025-03-26 14:55:06 +010026#include "perform_scan.hpp"
Alexander Hansen60803182025-06-27 14:41:28 +020027#include "phosphor-logging/lg2.hpp"
Benjamin Fairca2eb042022-09-13 06:40:42 +000028#include "topology.hpp"
Christopher Meis59ef1e72025-04-16 08:53:25 +020029#include "utils.hpp"
James Feist481c5d52019-08-13 14:40:40 -070030
James Feist11be6672018-04-06 14:05:32 -070031#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070032#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080033#include <boost/algorithm/string/predicate.hpp>
34#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070035#include <boost/algorithm/string/split.hpp>
James Feist02d2b932020-02-06 16:28:48 -080036#include <boost/asio/io_context.hpp>
Ed Tanous49a888c2023-03-06 13:44:51 -080037#include <boost/asio/post.hpp>
James Feistb1728ca2020-04-30 15:40:55 -070038#include <boost/asio/steady_timer.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080039#include <boost/container/flat_map.hpp>
40#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070041#include <boost/range/iterator_range.hpp>
James Feist8c505da2020-05-28 10:06:33 -070042#include <nlohmann/json.hpp>
43#include <sdbusplus/asio/connection.hpp>
44#include <sdbusplus/asio/object_server.hpp>
45
James Feist637b3ef2019-04-15 16:35:30 -070046#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080047#include <fstream>
Andrew Jefferye35d0ac2022-03-24 15:53:13 +103048#include <functional>
James Feista465ccc2019-02-08 12:51:01 -080049#include <iostream>
Andrew Jeffery666583b2021-12-01 15:50:12 +103050#include <map>
James Feista465ccc2019-02-08 12:51:01 -080051#include <regex>
James Feist1df06a42019-04-11 14:23:04 -070052constexpr const char* tempConfigDir = "/tmp/configuration/";
53constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
James Feistf1b14142019-04-10 15:22:09 -070054
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020055static constexpr std::array<const char*, 6> settableInterfaces = {
56 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist3cb5fec2018-01-23 14:41:51 -080057
Ed Tanous07d467b2021-02-23 14:48:37 -080058const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
59const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -080060
James Feista465ccc2019-02-08 12:51:01 -080061sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -070062{
63 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
64 interface) != settableInterfaces.end()
65 ? sdbusplus::asio::PropertyPermission::readWrite
66 : sdbusplus::asio::PropertyPermission::readOnly;
67}
68
Christopher Meiscf6a75b2025-06-03 07:53:50 +020069EntityManager::EntityManager(
Alexander Hansena555acf2025-06-27 11:59:10 +020070 std::shared_ptr<sdbusplus::asio::connection>& systemBus,
71 boost::asio::io_context& io) :
Christopher Meiscf6a75b2025-06-03 07:53:50 +020072 systemBus(systemBus),
73 objServer(sdbusplus::asio::object_server(systemBus, /*skipManager=*/true)),
74 lastJson(nlohmann::json::object()),
Alexander Hansenb1340da2025-06-27 14:29:13 +020075 systemConfiguration(nlohmann::json::object()), io(io),
76 propertiesChangedTimer(io)
Christopher Meiscf6a75b2025-06-03 07:53:50 +020077{
78 // All other objects that EntityManager currently support are under the
79 // inventory subtree.
80 // See the discussion at
81 // https://discord.com/channels/775381525260664832/1018929092009144380
82 objServer.add_manager("/xyz/openbmc_project/inventory");
James Feist75fdeeb2018-02-20 14:26:16 -080083
Christopher Meiscf6a75b2025-06-03 07:53:50 +020084 entityIface = objServer.add_interface("/xyz/openbmc_project/EntityManager",
85 "xyz.openbmc_project.EntityManager");
86 entityIface->register_method("ReScan", [this]() {
87 propertiesChangedCallback();
88 });
89 dbus_interface::tryIfaceInitialize(entityIface);
Christopher Meisf7252572025-06-11 13:22:05 +020090
91 initFilters(configuration.probeInterfaces);
Christopher Meiscf6a75b2025-06-03 07:53:50 +020092}
93
94void EntityManager::postToDbus(const nlohmann::json& newConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -080095{
Matt Spinler6eb60972023-08-14 16:36:20 -050096 std::map<std::string, std::string> newBoards; // path -> name
Benjamin Fairca2eb042022-09-13 06:40:42 +000097
James Feist97a63f12018-05-17 13:50:57 -070098 // iterate through boards
Patrick Williams2594d362022-09-28 06:46:24 -050099 for (const auto& [boardId, boardConfig] : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800100 {
Matt Spinler3d1909a2023-08-10 16:39:44 -0500101 std::string boardName = boardConfig["Name"];
102 std::string boardNameOrig = boardConfig["Name"];
Andrew Jeffery13132df2022-03-25 13:29:41 +1030103 std::string jsonPointerPath = "/" + boardId;
James Feist97a63f12018-05-17 13:50:57 -0700104 // loop through newConfiguration, but use values from system
105 // configuration to be able to modify via dbus later
Andrew Jeffery13132df2022-03-25 13:29:41 +1030106 auto boardValues = systemConfiguration[boardId];
James Feistd63d18a2018-07-19 15:23:45 -0700107 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800108 std::string boardType;
109 if (findBoardType != boardValues.end() &&
110 findBoardType->type() == nlohmann::json::value_t::string)
111 {
112 boardType = findBoardType->get<std::string>();
113 std::regex_replace(boardType.begin(), boardType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800114 boardType.end(), illegalDbusMemberRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800115 }
116 else
117 {
Matt Spinler3d1909a2023-08-10 16:39:44 -0500118 std::cerr << "Unable to find type for " << boardName
James Feist1b2e2242018-01-30 13:45:19 -0800119 << " reverting to Chassis.\n";
120 boardType = "Chassis";
121 }
James Feist11be6672018-04-06 14:05:32 -0700122 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800123
Matt Spinler3d1909a2023-08-10 16:39:44 -0500124 std::regex_replace(boardName.begin(), boardName.begin(),
125 boardName.end(), illegalDbusMemberRegex, "_");
126 std::string boardPath = "/xyz/openbmc_project/inventory/system/";
127 boardPath += boardtypeLower;
128 boardPath += "/";
129 boardPath += boardName;
James Feist1b2e2242018-01-30 13:45:19 -0800130
James Feistd58879a82019-09-11 11:26:07 -0700131 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
Alexander Hansen57604ed2025-06-27 13:22:28 +0200132 dbus_interface.createInterface(objServer, boardPath,
133 "xyz.openbmc_project.Inventory.Item",
134 boardName);
James Feist68500ff2018-08-08 15:40:42 -0700135
James Feistd58879a82019-09-11 11:26:07 -0700136 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
Alexander Hansen57604ed2025-06-27 13:22:28 +0200137 dbus_interface.createInterface(
Christopher Meis12bea9b2025-04-03 10:14:42 +0200138 objServer, boardPath,
139 "xyz.openbmc_project.Inventory.Item." + boardType,
140 boardNameOrig);
James Feist11be6672018-04-06 14:05:32 -0700141
Alexander Hansen57604ed2025-06-27 13:22:28 +0200142 dbus_interface.createAddObjectMethod(
Alexander Hansena555acf2025-06-27 11:59:10 +0200143 io, jsonPointerPath, boardPath, systemConfiguration, objServer,
Christopher Meis12bea9b2025-04-03 10:14:42 +0200144 boardNameOrig);
James Feist68500ff2018-08-08 15:40:42 -0700145
Christopher Meis12bea9b2025-04-03 10:14:42 +0200146 dbus_interface::populateInterfaceFromJson(
Alexander Hansena555acf2025-06-27 11:59:10 +0200147 io, systemConfiguration, jsonPointerPath, boardIface, boardValues,
Christopher Meis12bea9b2025-04-03 10:14:42 +0200148 objServer);
James Feist97a63f12018-05-17 13:50:57 -0700149 jsonPointerPath += "/";
150 // iterate through board properties
Patrick Williams2594d362022-09-28 06:46:24 -0500151 for (const auto& [propName, propValue] : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700152 {
Andrew Jefferya96950d2022-03-25 13:32:46 +1030153 if (propValue.type() == nlohmann::json::value_t::object)
James Feist11be6672018-04-06 14:05:32 -0700154 {
James Feistd58879a82019-09-11 11:26:07 -0700155 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
Alexander Hansen57604ed2025-06-27 13:22:28 +0200156 dbus_interface.createInterface(objServer, boardPath,
157 propName, boardNameOrig);
James Feistd58879a82019-09-11 11:26:07 -0700158
Christopher Meis12bea9b2025-04-03 10:14:42 +0200159 dbus_interface::populateInterfaceFromJson(
Alexander Hansena555acf2025-06-27 11:59:10 +0200160 io, systemConfiguration, jsonPointerPath + propName, iface,
Christopher Meis12bea9b2025-04-03 10:14:42 +0200161 propValue, objServer);
James Feist11be6672018-04-06 14:05:32 -0700162 }
163 }
James Feist97a63f12018-05-17 13:50:57 -0700164
James Feist1e3e6982018-08-03 16:09:28 -0700165 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800166 if (exposes == boardValues.end())
167 {
168 continue;
169 }
James Feist97a63f12018-05-17 13:50:57 -0700170 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700171 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700172
173 // store the board level pointer so we can modify it on the way down
174 std::string jsonPointerPathBoard = jsonPointerPath;
175 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -0800176 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -0800177 {
James Feist97a63f12018-05-17 13:50:57 -0700178 exposesIndex++;
179 jsonPointerPath = jsonPointerPathBoard;
180 jsonPointerPath += std::to_string(exposesIndex);
181
James Feistd63d18a2018-07-19 15:23:45 -0700182 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800183 if (findName == item.end())
184 {
185 std::cerr << "cannot find name in field " << item << "\n";
186 continue;
187 }
James Feist1e3e6982018-08-03 16:09:28 -0700188 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800189 // if status is not found it is assumed to be status = 'okay'
190 if (findStatus != item.end())
191 {
192 if (*findStatus == "disabled")
193 {
194 continue;
195 }
196 }
James Feistd63d18a2018-07-19 15:23:45 -0700197 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800198 std::string itemType;
199 if (findType != item.end())
200 {
201 itemType = findType->get<std::string>();
202 std::regex_replace(itemType.begin(), itemType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800203 itemType.end(), illegalDbusPathRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800204 }
205 else
206 {
207 itemType = "unknown";
208 }
209 std::string itemName = findName->get<std::string>();
210 std::regex_replace(itemName.begin(), itemName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800211 itemName.end(), illegalDbusMemberRegex, "_");
Matt Spinler3d1909a2023-08-10 16:39:44 -0500212 std::string ifacePath = boardPath;
Ed Tanous07d467b2021-02-23 14:48:37 -0800213 ifacePath += "/";
214 ifacePath += itemName;
James Feistc6248a52018-08-14 10:09:45 -0700215
Sui Chen74ebe592022-09-13 10:22:03 -0700216 if (itemType == "BMC")
217 {
218 std::shared_ptr<sdbusplus::asio::dbus_interface> bmcIface =
Alexander Hansen57604ed2025-06-27 13:22:28 +0200219 dbus_interface.createInterface(
Christopher Meis12bea9b2025-04-03 10:14:42 +0200220 objServer, ifacePath,
221 "xyz.openbmc_project.Inventory.Item.Bmc",
222 boardNameOrig);
223 dbus_interface::populateInterfaceFromJson(
Alexander Hansena555acf2025-06-27 11:59:10 +0200224 io, systemConfiguration, jsonPointerPath, bmcIface, item,
Christopher Meis12bea9b2025-04-03 10:14:42 +0200225 objServer, getPermission(itemType));
Sui Chen74ebe592022-09-13 10:22:03 -0700226 }
Edward Leeeb587b42023-03-08 18:59:04 +0000227 else if (itemType == "System")
228 {
229 std::shared_ptr<sdbusplus::asio::dbus_interface> systemIface =
Alexander Hansen57604ed2025-06-27 13:22:28 +0200230 dbus_interface.createInterface(
Christopher Meis12bea9b2025-04-03 10:14:42 +0200231 objServer, ifacePath,
232 "xyz.openbmc_project.Inventory.Item.System",
233 boardNameOrig);
234 dbus_interface::populateInterfaceFromJson(
Alexander Hansena555acf2025-06-27 11:59:10 +0200235 io, systemConfiguration, jsonPointerPath, systemIface, item,
Christopher Meis12bea9b2025-04-03 10:14:42 +0200236 objServer, getPermission(itemType));
Edward Leeeb587b42023-03-08 18:59:04 +0000237 }
Sui Chen74ebe592022-09-13 10:22:03 -0700238
Patrick Williams2594d362022-09-28 06:46:24 -0500239 for (const auto& [name, config] : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800240 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030241 jsonPointerPath = jsonPointerPathBoard;
242 jsonPointerPath.append(std::to_string(exposesIndex))
243 .append("/")
244 .append(name);
245 if (config.type() == nlohmann::json::value_t::object)
James Feist1b2e2242018-01-30 13:45:19 -0800246 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030247 std::string ifaceName =
248 "xyz.openbmc_project.Configuration.";
249 ifaceName.append(itemType).append(".").append(name);
James Feist97a63f12018-05-17 13:50:57 -0700250
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030251 std::shared_ptr<sdbusplus::asio::dbus_interface>
Alexander Hansen57604ed2025-06-27 13:22:28 +0200252 objectIface = dbus_interface.createInterface(
Christopher Meis12bea9b2025-04-03 10:14:42 +0200253 objServer, ifacePath, ifaceName, boardNameOrig);
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030254
Christopher Meis12bea9b2025-04-03 10:14:42 +0200255 dbus_interface::populateInterfaceFromJson(
Alexander Hansena555acf2025-06-27 11:59:10 +0200256 io, systemConfiguration, jsonPointerPath, objectIface,
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030257 config, objServer, getPermission(name));
James Feist1b2e2242018-01-30 13:45:19 -0800258 }
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030259 else if (config.type() == nlohmann::json::value_t::array)
James Feist1b2e2242018-01-30 13:45:19 -0800260 {
261 size_t index = 0;
Ed Tanous3013fb42022-07-09 08:27:06 -0700262 if (config.empty())
James Feist1b2e2242018-01-30 13:45:19 -0800263 {
James Feist8f2710a2018-05-09 17:18:55 -0700264 continue;
265 }
266 bool isLegal = true;
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030267 auto type = config[0].type();
James Feist8f2710a2018-05-09 17:18:55 -0700268 if (type != nlohmann::json::value_t::object)
269 {
270 continue;
271 }
272
273 // verify legal json
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030274 for (const auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700275 {
276 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800277 {
James Feist8f2710a2018-05-09 17:18:55 -0700278 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800279 break;
280 }
James Feist8f2710a2018-05-09 17:18:55 -0700281 }
282 if (!isLegal)
283 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030284 std::cerr << "dbus format error" << config << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700285 break;
286 }
287
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030288 for (auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700289 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030290 std::string ifaceName =
291 "xyz.openbmc_project.Configuration.";
292 ifaceName.append(itemType).append(".").append(name);
293 ifaceName.append(std::to_string(index));
James Feist97a63f12018-05-17 13:50:57 -0700294
James Feistd58879a82019-09-11 11:26:07 -0700295 std::shared_ptr<sdbusplus::asio::dbus_interface>
Alexander Hansen57604ed2025-06-27 13:22:28 +0200296 objectIface = dbus_interface.createInterface(
Matt Spinler3d1909a2023-08-10 16:39:44 -0500297 objServer, ifacePath, ifaceName, boardNameOrig);
James Feistd58879a82019-09-11 11:26:07 -0700298
Christopher Meis12bea9b2025-04-03 10:14:42 +0200299 dbus_interface::populateInterfaceFromJson(
Alexander Hansena555acf2025-06-27 11:59:10 +0200300 io, systemConfiguration,
James Feistc6248a52018-08-14 10:09:45 -0700301 jsonPointerPath + "/" + std::to_string(index),
302 objectIface, arrayItem, objServer,
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030303 getPermission(name));
James Feistbb43d022018-06-12 15:44:33 -0700304 index++;
James Feist1b2e2242018-01-30 13:45:19 -0800305 }
306 }
307 }
Benjamin Fairca2eb042022-09-13 06:40:42 +0000308
George Liu5c1a61a2024-10-24 18:02:29 +0800309 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
Alexander Hansen57604ed2025-06-27 13:22:28 +0200310 dbus_interface.createInterface(
Christopher Meis12bea9b2025-04-03 10:14:42 +0200311 objServer, ifacePath,
312 "xyz.openbmc_project.Configuration." + itemType,
313 boardNameOrig);
George Liu5c1a61a2024-10-24 18:02:29 +0800314
Christopher Meis12bea9b2025-04-03 10:14:42 +0200315 dbus_interface::populateInterfaceFromJson(
Alexander Hansena555acf2025-06-27 11:59:10 +0200316 io, systemConfiguration, jsonPointerPath, itemIface, item,
Christopher Meis12bea9b2025-04-03 10:14:42 +0200317 objServer, getPermission(itemType));
George Liu5c1a61a2024-10-24 18:02:29 +0800318
Matt Spinler6eb60972023-08-14 16:36:20 -0500319 topology.addBoard(boardPath, boardType, boardNameOrig, item);
James Feist1b2e2242018-01-30 13:45:19 -0800320 }
Matt Spinler6eb60972023-08-14 16:36:20 -0500321
322 newBoards.emplace(boardPath, boardNameOrig);
James Feist1b2e2242018-01-30 13:45:19 -0800323 }
Benjamin Fairca2eb042022-09-13 06:40:42 +0000324
Matt Spinler6eb60972023-08-14 16:36:20 -0500325 for (const auto& [assocPath, assocPropValue] :
326 topology.getAssocs(newBoards))
Benjamin Fairca2eb042022-09-13 06:40:42 +0000327 {
Matt Spinler6eb60972023-08-14 16:36:20 -0500328 auto findBoard = newBoards.find(assocPath);
329 if (findBoard == newBoards.end())
330 {
331 continue;
332 }
Benjamin Fairca2eb042022-09-13 06:40:42 +0000333
Alexander Hansen57604ed2025-06-27 13:22:28 +0200334 auto ifacePtr = dbus_interface.createInterface(
Matt Spinler6eb60972023-08-14 16:36:20 -0500335 objServer, assocPath, "xyz.openbmc_project.Association.Definitions",
336 findBoard->second);
337
338 ifacePtr->register_property("Associations", assocPropValue);
Christopher Meis12bea9b2025-04-03 10:14:42 +0200339 dbus_interface::tryIfaceInitialize(ifacePtr);
Benjamin Fairca2eb042022-09-13 06:40:42 +0000340 }
James Feist1b2e2242018-01-30 13:45:19 -0800341}
342
Andrew Jeffery55192932022-03-24 12:29:27 +1030343static bool deviceRequiresPowerOn(const nlohmann::json& entity)
344{
345 auto powerState = entity.find("PowerState");
Andrew Jefferyb6209442022-03-24 12:36:20 +1030346 if (powerState == entity.end())
Andrew Jeffery55192932022-03-24 12:29:27 +1030347 {
Andrew Jefferyb6209442022-03-24 12:36:20 +1030348 return false;
Andrew Jeffery55192932022-03-24 12:29:27 +1030349 }
350
Ed Tanous3013fb42022-07-09 08:27:06 -0700351 const auto* ptr = powerState->get_ptr<const std::string*>();
352 if (ptr == nullptr)
Andrew Jefferyb6209442022-03-24 12:36:20 +1030353 {
354 return false;
355 }
356
357 return *ptr == "On" || *ptr == "BiosPost";
Andrew Jeffery55192932022-03-24 12:29:27 +1030358}
359
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030360static void pruneDevice(const nlohmann::json& systemConfiguration,
361 const bool powerOff, const bool scannedPowerOff,
362 const std::string& name, const nlohmann::json& device)
363{
364 if (systemConfiguration.contains(name))
365 {
366 return;
367 }
368
Andrew Jeffery4db38bc2022-03-24 13:42:41 +1030369 if (deviceRequiresPowerOn(device) && (powerOff || scannedPowerOff))
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030370 {
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030371 return;
372 }
373
374 logDeviceRemoved(device);
375}
376
Alexander Hansen95ab18f2025-06-27 13:58:10 +0200377void EntityManager::startRemovedTimer(boost::asio::steady_timer& timer,
378 nlohmann::json& systemConfiguration)
James Feist1df06a42019-04-11 14:23:04 -0700379{
James Feistfb00f392019-06-25 14:16:48 -0700380 if (systemConfiguration.empty() || lastJson.empty())
381 {
382 return; // not ready yet
383 }
James Feist1df06a42019-04-11 14:23:04 -0700384 if (scannedPowerOn)
385 {
386 return;
387 }
388
Alexander Hansen0ab32b32025-06-27 14:50:33 +0200389 if (!powerStatus.isPowerOn() && scannedPowerOff)
James Feist1df06a42019-04-11 14:23:04 -0700390 {
391 return;
392 }
393
James Feistb1728ca2020-04-30 15:40:55 -0700394 timer.expires_after(std::chrono::seconds(10));
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030395 timer.async_wait(
Alexander Hansen95ab18f2025-06-27 13:58:10 +0200396 [&systemConfiguration, this](const boost::system::error_code& ec) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400397 if (ec == boost::asio::error::operation_aborted)
398 {
399 return;
400 }
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030401
Alexander Hansen0ab32b32025-06-27 14:50:33 +0200402 bool powerOff = !powerStatus.isPowerOn();
Patrick Williamsb7077432024-08-16 15:22:21 -0400403 for (const auto& [name, device] : lastJson.items())
404 {
405 pruneDevice(systemConfiguration, powerOff, scannedPowerOff,
406 name, device);
407 }
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030408
Patrick Williamsb7077432024-08-16 15:22:21 -0400409 scannedPowerOff = true;
410 if (!powerOff)
411 {
412 scannedPowerOn = true;
413 }
414 });
James Feist1df06a42019-04-11 14:23:04 -0700415}
416
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200417void EntityManager::pruneConfiguration(bool powerOff, const std::string& name,
418 const nlohmann::json& device)
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030419{
420 if (powerOff && deviceRequiresPowerOn(device))
421 {
422 // power not on yet, don't know if it's there or not
423 return;
424 }
425
Alexander Hansen57604ed2025-06-27 13:22:28 +0200426 auto& ifaces = dbus_interface.getDeviceInterfaces(device);
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030427 for (auto& iface : ifaces)
428 {
429 auto sharedPtr = iface.lock();
430 if (!!sharedPtr)
431 {
432 objServer.remove_interface(sharedPtr);
433 }
434 }
435
436 ifaces.clear();
437 systemConfiguration.erase(name);
Matt Spinler6eb60972023-08-14 16:36:20 -0500438 topology.remove(device["Name"].get<std::string>());
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030439 logDeviceRemoved(device);
440}
441
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200442void EntityManager::publishNewConfiguration(
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030443 const size_t& instance, const size_t count,
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200444 boost::asio::steady_timer& timer, // Gerrit discussion:
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030445 // https://gerrit.openbmc-project.xyz/c/openbmc/entity-manager/+/52316/6
446 //
447 // Discord discussion:
448 // https://discord.com/channels/775381525260664832/867820390406422538/958048437729910854
449 //
450 // NOLINTNEXTLINE(performance-unnecessary-value-param)
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200451 const nlohmann::json newConfiguration)
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030452{
Alexander Hansena555acf2025-06-27 11:59:10 +0200453 loadOverlays(newConfiguration, io);
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030454
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200455 boost::asio::post(io, [this]() {
Christopher Meisf7252572025-06-11 13:22:05 +0200456 if (!writeJsonFiles(systemConfiguration))
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030457 {
458 std::cerr << "Error writing json files\n";
459 }
460 });
461
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200462 boost::asio::post(io, [this, &instance, count, &timer, newConfiguration]() {
463 postToDbus(newConfiguration);
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030464 if (count == instance)
465 {
Alexander Hansen95ab18f2025-06-27 13:58:10 +0200466 startRemovedTimer(timer, systemConfiguration);
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030467 }
468 });
469}
470
James Feist8f2710a2018-05-09 17:18:55 -0700471// main properties changed entry
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200472void EntityManager::propertiesChangedCallback()
James Feist8f2710a2018-05-09 17:18:55 -0700473{
Alexander Hansenfc1b1e22025-06-27 14:34:11 +0200474 propertiesChangedInstance++;
475 size_t count = propertiesChangedInstance;
James Feist1df06a42019-04-11 14:23:04 -0700476
Alexander Hansenb1340da2025-06-27 14:29:13 +0200477 propertiesChangedTimer.expires_after(std::chrono::milliseconds(500));
James Feist8f2710a2018-05-09 17:18:55 -0700478
479 // setup an async wait as we normally get flooded with new requests
Alexander Hansenb1340da2025-06-27 14:29:13 +0200480 propertiesChangedTimer.async_wait(
481 [this, count](const boost::system::error_code& ec) {
482 if (ec == boost::asio::error::operation_aborted)
483 {
484 // we were cancelled
485 return;
486 }
487 if (ec)
488 {
489 std::cerr << "async wait error " << ec << "\n";
490 return;
491 }
James Feist8f2710a2018-05-09 17:18:55 -0700492
Alexander Hansenb1340da2025-06-27 14:29:13 +0200493 if (propertiesChangedInProgress)
494 {
495 propertiesChangedCallback();
496 return;
497 }
498 propertiesChangedInProgress = true;
James Feist2539ccd2020-05-01 16:15:08 -0700499
Alexander Hansenb1340da2025-06-27 14:29:13 +0200500 nlohmann::json oldConfiguration = systemConfiguration;
501 auto missingConfigurations = std::make_shared<nlohmann::json>();
502 *missingConfigurations = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -0700503
Alexander Hansenb1340da2025-06-27 14:29:13 +0200504 auto perfScan = std::make_shared<scan::PerformScan>(
Christopher Meisf7252572025-06-11 13:22:05 +0200505 *this, *missingConfigurations, configuration.configurations, io,
Alexander Hansenb1340da2025-06-27 14:29:13 +0200506 [this, count, oldConfiguration, missingConfigurations]() {
507 // this is something that since ac has been applied to the
508 // bmc we saw, and we no longer see it
Alexander Hansen0ab32b32025-06-27 14:50:33 +0200509 bool powerOff = !powerStatus.isPowerOn();
Alexander Hansenb1340da2025-06-27 14:29:13 +0200510 for (const auto& [name, device] :
511 missingConfigurations->items())
512 {
513 pruneConfiguration(powerOff, name, device);
514 }
Alexander Hansenb1340da2025-06-27 14:29:13 +0200515 nlohmann::json newConfiguration = systemConfiguration;
516
Christopher Meisf7252572025-06-11 13:22:05 +0200517 deriveNewConfiguration(oldConfiguration, newConfiguration);
Alexander Hansenb1340da2025-06-27 14:29:13 +0200518
519 for (const auto& [_, device] : newConfiguration.items())
520 {
521 logDeviceAdded(device);
522 }
523
524 propertiesChangedInProgress = false;
525
526 boost::asio::post(io, [this, newConfiguration, count] {
527 publishNewConfiguration(
Alexander Hansenfc1b1e22025-06-27 14:34:11 +0200528 std::ref(propertiesChangedInstance), count,
Alexander Hansenb1340da2025-06-27 14:29:13 +0200529 std::ref(propertiesChangedTimer), newConfiguration);
530 });
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200531 });
Alexander Hansenb1340da2025-06-27 14:29:13 +0200532 perfScan->run();
533 });
James Feist75fdeeb2018-02-20 14:26:16 -0800534}
535
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600536// Check if InterfacesAdded payload contains an iface that needs probing.
Patrick Williamsb7077432024-08-16 15:22:21 -0400537static bool iaContainsProbeInterface(
Christopher Meisf7252572025-06-11 13:22:05 +0200538 sdbusplus::message_t& msg,
539 const std::unordered_set<std::string>& probeInterfaces)
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600540{
541 sdbusplus::message::object_path path;
542 DBusObject interfaces;
543 std::set<std::string> interfaceSet;
544 std::set<std::string> intersect;
545
546 msg.read(path, interfaces);
547
548 std::for_each(interfaces.begin(), interfaces.end(),
549 [&interfaceSet](const auto& iface) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400550 interfaceSet.insert(iface.first);
551 });
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600552
553 std::set_intersection(interfaceSet.begin(), interfaceSet.end(),
554 probeInterfaces.begin(), probeInterfaces.end(),
555 std::inserter(intersect, intersect.end()));
556 return !intersect.empty();
557}
558
559// Check if InterfacesRemoved payload contains an iface that needs probing.
Patrick Williamsb7077432024-08-16 15:22:21 -0400560static bool irContainsProbeInterface(
Christopher Meisf7252572025-06-11 13:22:05 +0200561 sdbusplus::message_t& msg,
562 const std::unordered_set<std::string>& probeInterfaces)
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600563{
564 sdbusplus::message::object_path path;
565 std::set<std::string> interfaces;
566 std::set<std::string> intersect;
567
568 msg.read(path, interfaces);
569
570 std::set_intersection(interfaces.begin(), interfaces.end(),
571 probeInterfaces.begin(), probeInterfaces.end(),
572 std::inserter(intersect, intersect.end()));
573 return !intersect.empty();
574}
575
Alexander Hansenad28e402025-06-25 12:38:20 +0200576void EntityManager::handleCurrentConfigurationJson()
577{
578 if (em_utils::fwVersionIsSame())
579 {
Christopher Meisf7252572025-06-11 13:22:05 +0200580 if (std::filesystem::is_regular_file(currentConfiguration))
Alexander Hansenad28e402025-06-25 12:38:20 +0200581 {
582 // this file could just be deleted, but it's nice for debug
583 std::filesystem::create_directory(tempConfigDir);
584 std::filesystem::remove(lastConfiguration);
Christopher Meisf7252572025-06-11 13:22:05 +0200585 std::filesystem::copy(currentConfiguration, lastConfiguration);
586 std::filesystem::remove(currentConfiguration);
Alexander Hansenad28e402025-06-25 12:38:20 +0200587
588 std::ifstream jsonStream(lastConfiguration);
589 if (jsonStream.good())
590 {
591 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
592 if (data.is_discarded())
593 {
594 std::cerr
595 << "syntax error in " << lastConfiguration << "\n";
596 }
597 else
598 {
599 lastJson = std::move(data);
600 }
601 }
602 else
603 {
604 std::cerr << "unable to open " << lastConfiguration << "\n";
605 }
606 }
607 }
608 else
609 {
610 // not an error, just logging at this level to make it in the journal
611 std::cerr << "Clearing previous configuration\n";
Christopher Meisf7252572025-06-11 13:22:05 +0200612 std::filesystem::remove(currentConfiguration);
Alexander Hansenad28e402025-06-25 12:38:20 +0200613 }
614}
615
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200616void EntityManager::registerCallback(const std::string& path)
James Feist75fdeeb2018-02-20 14:26:16 -0800617{
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200618 if (dbusMatches.contains(path))
619 {
620 return;
621 }
Nan Zhoua3315672022-09-20 19:48:14 +0000622
Alexander Hansen60803182025-06-27 14:41:28 +0200623 lg2::debug("creating PropertiesChanged match on {PATH}", "PATH", path);
624
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200625 std::function<void(sdbusplus::message_t & message)> eventHandler =
626 [&](sdbusplus::message_t&) { propertiesChangedCallback(); };
James Feistfd1264a2018-05-03 12:10:00 -0700627
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200628 sdbusplus::bus::match_t match(
629 static_cast<sdbusplus::bus_t&>(*systemBus),
630 "type='signal',member='PropertiesChanged',path='" + path + "'",
631 eventHandler);
632 dbusMatches.emplace(path, std::move(match));
633}
James Feistfd1264a2018-05-03 12:10:00 -0700634
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200635// We need a poke from DBus for static providers that create all their
636// objects prior to claiming a well-known name, and thus don't emit any
637// org.freedesktop.DBus.Properties signals. Similarly if a process exits
638// for any reason, expected or otherwise, we'll need a poke to remove
639// entities from DBus.
Christopher Meisf7252572025-06-11 13:22:05 +0200640void EntityManager::initFilters(
641 const std::unordered_set<std::string>& probeInterfaces)
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200642{
Alexander Hansen0f7bd782025-06-27 13:39:53 +0200643 nameOwnerChangedMatch = std::make_unique<sdbusplus::bus::match_t>(
Patrick Williams2af39222022-07-22 19:26:56 -0500644 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishopc76af0f2020-12-04 13:50:23 -0500645 sdbusplus::bus::match::rules::nameOwnerChanged(),
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200646 [this](sdbusplus::message_t& m) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400647 auto [name, oldOwner,
648 newOwner] = m.unpack<std::string, std::string, std::string>();
Patrick Williams7b8786f2022-10-10 10:23:37 -0500649
Patrick Williamsb7077432024-08-16 15:22:21 -0400650 if (name.starts_with(':'))
651 {
652 // We should do nothing with unique-name connections.
653 return;
654 }
Patrick Williams7b8786f2022-10-10 10:23:37 -0500655
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200656 propertiesChangedCallback();
Patrick Williamsb7077432024-08-16 15:22:21 -0400657 });
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200658
Brad Bishop10a8c5f2020-12-07 21:40:07 -0500659 // We also need a poke from DBus when new interfaces are created or
660 // destroyed.
Alexander Hansen0f7bd782025-06-27 13:39:53 +0200661 interfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
Patrick Williams2af39222022-07-22 19:26:56 -0500662 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -0500663 sdbusplus::bus::match::rules::interfacesAdded(),
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200664 [this, probeInterfaces](sdbusplus::message_t& msg) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400665 if (iaContainsProbeInterface(msg, probeInterfaces))
666 {
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200667 propertiesChangedCallback();
Patrick Williamsb7077432024-08-16 15:22:21 -0400668 }
669 });
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200670
Alexander Hansen0f7bd782025-06-27 13:39:53 +0200671 interfacesRemovedMatch = std::make_unique<sdbusplus::bus::match_t>(
Patrick Williams2af39222022-07-22 19:26:56 -0500672 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -0500673 sdbusplus::bus::match::rules::interfacesRemoved(),
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200674 [this, probeInterfaces](sdbusplus::message_t& msg) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400675 if (irContainsProbeInterface(msg, probeInterfaces))
676 {
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200677 propertiesChangedCallback();
Patrick Williamsb7077432024-08-16 15:22:21 -0400678 }
679 });
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200680}