blob: 2b656045748d26a397b3125feecd787c33ae3dfd [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"
Brad Bishope45d8c72022-05-25 15:12:53 -040024#include "overlay.hpp"
Christopher Meis26fbbd52025-03-26 14:55:06 +010025#include "perform_scan.hpp"
Benjamin Fairca2eb042022-09-13 06:40:42 +000026#include "topology.hpp"
Christopher Meis59ef1e72025-04-16 08:53:25 +020027#include "utils.hpp"
James Feist481c5d52019-08-13 14:40:40 -070028
James Feist11be6672018-04-06 14:05:32 -070029#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070030#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080031#include <boost/algorithm/string/predicate.hpp>
32#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070033#include <boost/algorithm/string/split.hpp>
James Feist02d2b932020-02-06 16:28:48 -080034#include <boost/asio/io_context.hpp>
Ed Tanous49a888c2023-03-06 13:44:51 -080035#include <boost/asio/post.hpp>
James Feistb1728ca2020-04-30 15:40:55 -070036#include <boost/asio/steady_timer.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080037#include <boost/container/flat_map.hpp>
38#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070039#include <boost/range/iterator_range.hpp>
James Feist8c505da2020-05-28 10:06:33 -070040#include <nlohmann/json.hpp>
41#include <sdbusplus/asio/connection.hpp>
42#include <sdbusplus/asio/object_server.hpp>
43
James Feist637b3ef2019-04-15 16:35:30 -070044#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080045#include <fstream>
Andrew Jefferye35d0ac2022-03-24 15:53:13 +103046#include <functional>
James Feista465ccc2019-02-08 12:51:01 -080047#include <iostream>
Andrew Jeffery666583b2021-12-01 15:50:12 +103048#include <map>
James Feista465ccc2019-02-08 12:51:01 -080049#include <regex>
James Feist1df06a42019-04-11 14:23:04 -070050constexpr const char* tempConfigDir = "/tmp/configuration/";
51constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
James Feistf1b14142019-04-10 15:22:09 -070052
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020053static constexpr std::array<const char*, 6> settableInterfaces = {
54 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist3cb5fec2018-01-23 14:41:51 -080055
Ed Tanousfc171422024-04-04 17:18:16 -070056// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
James Feistd58879a2019-09-11 11:26:07 -070057
James Feist3cb5fec2018-01-23 14:41:51 -080058// todo: pass this through nicer
Ed Tanous07d467b2021-02-23 14:48:37 -080059std::shared_ptr<sdbusplus::asio::connection> systemBus;
Andrew Jeffery47af65a2021-12-01 14:16:31 +103060nlohmann::json lastJson;
Matt Spinler6eb60972023-08-14 16:36:20 -050061Topology topology;
James Feist3cb5fec2018-01-23 14:41:51 -080062
James Feist02d2b932020-02-06 16:28:48 -080063boost::asio::io_context io;
Ed Tanousfc171422024-04-04 17:18:16 -070064// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
James Feist02d2b932020-02-06 16:28:48 -080065
Ed Tanous07d467b2021-02-23 14:48:37 -080066const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
67const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -080068
James Feista465ccc2019-02-08 12:51:01 -080069sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -070070{
71 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
72 interface) != settableInterfaces.end()
73 ? sdbusplus::asio::PropertyPermission::readWrite
74 : sdbusplus::asio::PropertyPermission::readOnly;
75}
76
James Feista465ccc2019-02-08 12:51:01 -080077void postToDbus(const nlohmann::json& newConfiguration,
78 nlohmann::json& systemConfiguration,
79 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -080080
James Feist1b2e2242018-01-30 13:45:19 -080081{
Matt Spinler6eb60972023-08-14 16:36:20 -050082 std::map<std::string, std::string> newBoards; // path -> name
Benjamin Fairca2eb042022-09-13 06:40:42 +000083
James Feist97a63f12018-05-17 13:50:57 -070084 // iterate through boards
Patrick Williams2594d362022-09-28 06:46:24 -050085 for (const auto& [boardId, boardConfig] : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -080086 {
Matt Spinler3d1909a2023-08-10 16:39:44 -050087 std::string boardName = boardConfig["Name"];
88 std::string boardNameOrig = boardConfig["Name"];
Andrew Jeffery13132df2022-03-25 13:29:41 +103089 std::string jsonPointerPath = "/" + boardId;
James Feist97a63f12018-05-17 13:50:57 -070090 // loop through newConfiguration, but use values from system
91 // configuration to be able to modify via dbus later
Andrew Jeffery13132df2022-03-25 13:29:41 +103092 auto boardValues = systemConfiguration[boardId];
James Feistd63d18a2018-07-19 15:23:45 -070093 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -080094 std::string boardType;
95 if (findBoardType != boardValues.end() &&
96 findBoardType->type() == nlohmann::json::value_t::string)
97 {
98 boardType = findBoardType->get<std::string>();
99 std::regex_replace(boardType.begin(), boardType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800100 boardType.end(), illegalDbusMemberRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800101 }
102 else
103 {
Matt Spinler3d1909a2023-08-10 16:39:44 -0500104 std::cerr << "Unable to find type for " << boardName
James Feist1b2e2242018-01-30 13:45:19 -0800105 << " reverting to Chassis.\n";
106 boardType = "Chassis";
107 }
James Feist11be6672018-04-06 14:05:32 -0700108 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800109
Matt Spinler3d1909a2023-08-10 16:39:44 -0500110 std::regex_replace(boardName.begin(), boardName.begin(),
111 boardName.end(), illegalDbusMemberRegex, "_");
112 std::string boardPath = "/xyz/openbmc_project/inventory/system/";
113 boardPath += boardtypeLower;
114 boardPath += "/";
115 boardPath += boardName;
James Feist1b2e2242018-01-30 13:45:19 -0800116
James Feistd58879a2019-09-11 11:26:07 -0700117 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
Christopher Meis12bea9b2025-04-03 10:14:42 +0200118 dbus_interface::createInterface(
119 objServer, boardPath, "xyz.openbmc_project.Inventory.Item",
120 boardName);
James Feist68500ff2018-08-08 15:40:42 -0700121
James Feistd58879a2019-09-11 11:26:07 -0700122 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
Christopher Meis12bea9b2025-04-03 10:14:42 +0200123 dbus_interface::createInterface(
124 objServer, boardPath,
125 "xyz.openbmc_project.Inventory.Item." + boardType,
126 boardNameOrig);
James Feist11be6672018-04-06 14:05:32 -0700127
Christopher Meis12bea9b2025-04-03 10:14:42 +0200128 dbus_interface::createAddObjectMethod(
129 jsonPointerPath, boardPath, systemConfiguration, objServer,
130 boardNameOrig);
James Feist68500ff2018-08-08 15:40:42 -0700131
Christopher Meis12bea9b2025-04-03 10:14:42 +0200132 dbus_interface::populateInterfaceFromJson(
133 systemConfiguration, jsonPointerPath, boardIface, boardValues,
134 objServer);
James Feist97a63f12018-05-17 13:50:57 -0700135 jsonPointerPath += "/";
136 // iterate through board properties
Patrick Williams2594d362022-09-28 06:46:24 -0500137 for (const auto& [propName, propValue] : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700138 {
Andrew Jefferya96950d2022-03-25 13:32:46 +1030139 if (propValue.type() == nlohmann::json::value_t::object)
James Feist11be6672018-04-06 14:05:32 -0700140 {
James Feistd58879a2019-09-11 11:26:07 -0700141 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
Christopher Meis12bea9b2025-04-03 10:14:42 +0200142 dbus_interface::createInterface(objServer, boardPath,
143 propName, boardNameOrig);
James Feistd58879a2019-09-11 11:26:07 -0700144
Christopher Meis12bea9b2025-04-03 10:14:42 +0200145 dbus_interface::populateInterfaceFromJson(
146 systemConfiguration, jsonPointerPath + propName, iface,
147 propValue, objServer);
James Feist11be6672018-04-06 14:05:32 -0700148 }
149 }
James Feist97a63f12018-05-17 13:50:57 -0700150
James Feist1e3e6982018-08-03 16:09:28 -0700151 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800152 if (exposes == boardValues.end())
153 {
154 continue;
155 }
James Feist97a63f12018-05-17 13:50:57 -0700156 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700157 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700158
159 // store the board level pointer so we can modify it on the way down
160 std::string jsonPointerPathBoard = jsonPointerPath;
161 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -0800162 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -0800163 {
James Feist97a63f12018-05-17 13:50:57 -0700164 exposesIndex++;
165 jsonPointerPath = jsonPointerPathBoard;
166 jsonPointerPath += std::to_string(exposesIndex);
167
James Feistd63d18a2018-07-19 15:23:45 -0700168 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800169 if (findName == item.end())
170 {
171 std::cerr << "cannot find name in field " << item << "\n";
172 continue;
173 }
James Feist1e3e6982018-08-03 16:09:28 -0700174 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800175 // if status is not found it is assumed to be status = 'okay'
176 if (findStatus != item.end())
177 {
178 if (*findStatus == "disabled")
179 {
180 continue;
181 }
182 }
James Feistd63d18a2018-07-19 15:23:45 -0700183 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800184 std::string itemType;
185 if (findType != item.end())
186 {
187 itemType = findType->get<std::string>();
188 std::regex_replace(itemType.begin(), itemType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800189 itemType.end(), illegalDbusPathRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800190 }
191 else
192 {
193 itemType = "unknown";
194 }
195 std::string itemName = findName->get<std::string>();
196 std::regex_replace(itemName.begin(), itemName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800197 itemName.end(), illegalDbusMemberRegex, "_");
Matt Spinler3d1909a2023-08-10 16:39:44 -0500198 std::string ifacePath = boardPath;
Ed Tanous07d467b2021-02-23 14:48:37 -0800199 ifacePath += "/";
200 ifacePath += itemName;
James Feistc6248a52018-08-14 10:09:45 -0700201
Sui Chen74ebe592022-09-13 10:22:03 -0700202 if (itemType == "BMC")
203 {
204 std::shared_ptr<sdbusplus::asio::dbus_interface> bmcIface =
Christopher Meis12bea9b2025-04-03 10:14:42 +0200205 dbus_interface::createInterface(
206 objServer, ifacePath,
207 "xyz.openbmc_project.Inventory.Item.Bmc",
208 boardNameOrig);
209 dbus_interface::populateInterfaceFromJson(
210 systemConfiguration, jsonPointerPath, bmcIface, item,
211 objServer, getPermission(itemType));
Sui Chen74ebe592022-09-13 10:22:03 -0700212 }
Edward Leeeb587b42023-03-08 18:59:04 +0000213 else if (itemType == "System")
214 {
215 std::shared_ptr<sdbusplus::asio::dbus_interface> systemIface =
Christopher Meis12bea9b2025-04-03 10:14:42 +0200216 dbus_interface::createInterface(
217 objServer, ifacePath,
218 "xyz.openbmc_project.Inventory.Item.System",
219 boardNameOrig);
220 dbus_interface::populateInterfaceFromJson(
221 systemConfiguration, jsonPointerPath, systemIface, item,
222 objServer, getPermission(itemType));
Edward Leeeb587b42023-03-08 18:59:04 +0000223 }
Sui Chen74ebe592022-09-13 10:22:03 -0700224
Patrick Williams2594d362022-09-28 06:46:24 -0500225 for (const auto& [name, config] : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800226 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030227 jsonPointerPath = jsonPointerPathBoard;
228 jsonPointerPath.append(std::to_string(exposesIndex))
229 .append("/")
230 .append(name);
231 if (config.type() == nlohmann::json::value_t::object)
James Feist1b2e2242018-01-30 13:45:19 -0800232 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030233 std::string ifaceName =
234 "xyz.openbmc_project.Configuration.";
235 ifaceName.append(itemType).append(".").append(name);
James Feist97a63f12018-05-17 13:50:57 -0700236
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030237 std::shared_ptr<sdbusplus::asio::dbus_interface>
Christopher Meis12bea9b2025-04-03 10:14:42 +0200238 objectIface = dbus_interface::createInterface(
239 objServer, ifacePath, ifaceName, boardNameOrig);
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030240
Christopher Meis12bea9b2025-04-03 10:14:42 +0200241 dbus_interface::populateInterfaceFromJson(
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030242 systemConfiguration, jsonPointerPath, objectIface,
243 config, objServer, getPermission(name));
James Feist1b2e2242018-01-30 13:45:19 -0800244 }
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030245 else if (config.type() == nlohmann::json::value_t::array)
James Feist1b2e2242018-01-30 13:45:19 -0800246 {
247 size_t index = 0;
Ed Tanous3013fb42022-07-09 08:27:06 -0700248 if (config.empty())
James Feist1b2e2242018-01-30 13:45:19 -0800249 {
James Feist8f2710a2018-05-09 17:18:55 -0700250 continue;
251 }
252 bool isLegal = true;
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030253 auto type = config[0].type();
James Feist8f2710a2018-05-09 17:18:55 -0700254 if (type != nlohmann::json::value_t::object)
255 {
256 continue;
257 }
258
259 // verify legal json
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030260 for (const auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700261 {
262 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800263 {
James Feist8f2710a2018-05-09 17:18:55 -0700264 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800265 break;
266 }
James Feist8f2710a2018-05-09 17:18:55 -0700267 }
268 if (!isLegal)
269 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030270 std::cerr << "dbus format error" << config << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700271 break;
272 }
273
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030274 for (auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700275 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030276 std::string ifaceName =
277 "xyz.openbmc_project.Configuration.";
278 ifaceName.append(itemType).append(".").append(name);
279 ifaceName.append(std::to_string(index));
James Feist97a63f12018-05-17 13:50:57 -0700280
James Feistd58879a2019-09-11 11:26:07 -0700281 std::shared_ptr<sdbusplus::asio::dbus_interface>
Christopher Meis12bea9b2025-04-03 10:14:42 +0200282 objectIface = dbus_interface::createInterface(
Matt Spinler3d1909a2023-08-10 16:39:44 -0500283 objServer, ifacePath, ifaceName, boardNameOrig);
James Feistd58879a2019-09-11 11:26:07 -0700284
Christopher Meis12bea9b2025-04-03 10:14:42 +0200285 dbus_interface::populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -0700286 systemConfiguration,
287 jsonPointerPath + "/" + std::to_string(index),
288 objectIface, arrayItem, objServer,
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030289 getPermission(name));
James Feistbb43d022018-06-12 15:44:33 -0700290 index++;
James Feist1b2e2242018-01-30 13:45:19 -0800291 }
292 }
293 }
Benjamin Fairca2eb042022-09-13 06:40:42 +0000294
George Liu5c1a61a2024-10-24 18:02:29 +0800295 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
Christopher Meis12bea9b2025-04-03 10:14:42 +0200296 dbus_interface::createInterface(
297 objServer, ifacePath,
298 "xyz.openbmc_project.Configuration." + itemType,
299 boardNameOrig);
George Liu5c1a61a2024-10-24 18:02:29 +0800300
Christopher Meis12bea9b2025-04-03 10:14:42 +0200301 dbus_interface::populateInterfaceFromJson(
302 systemConfiguration, jsonPointerPath, itemIface, item,
303 objServer, getPermission(itemType));
George Liu5c1a61a2024-10-24 18:02:29 +0800304
Matt Spinler6eb60972023-08-14 16:36:20 -0500305 topology.addBoard(boardPath, boardType, boardNameOrig, item);
James Feist1b2e2242018-01-30 13:45:19 -0800306 }
Matt Spinler6eb60972023-08-14 16:36:20 -0500307
308 newBoards.emplace(boardPath, boardNameOrig);
James Feist1b2e2242018-01-30 13:45:19 -0800309 }
Benjamin Fairca2eb042022-09-13 06:40:42 +0000310
Matt Spinler6eb60972023-08-14 16:36:20 -0500311 for (const auto& [assocPath, assocPropValue] :
312 topology.getAssocs(newBoards))
Benjamin Fairca2eb042022-09-13 06:40:42 +0000313 {
Matt Spinler6eb60972023-08-14 16:36:20 -0500314 auto findBoard = newBoards.find(assocPath);
315 if (findBoard == newBoards.end())
316 {
317 continue;
318 }
Benjamin Fairca2eb042022-09-13 06:40:42 +0000319
Christopher Meis12bea9b2025-04-03 10:14:42 +0200320 auto ifacePtr = dbus_interface::createInterface(
Matt Spinler6eb60972023-08-14 16:36:20 -0500321 objServer, assocPath, "xyz.openbmc_project.Association.Definitions",
322 findBoard->second);
323
324 ifacePtr->register_property("Associations", assocPropValue);
Christopher Meis12bea9b2025-04-03 10:14:42 +0200325 dbus_interface::tryIfaceInitialize(ifacePtr);
Benjamin Fairca2eb042022-09-13 06:40:42 +0000326 }
James Feist1b2e2242018-01-30 13:45:19 -0800327}
328
Andrew Jeffery55192932022-03-24 12:29:27 +1030329static bool deviceRequiresPowerOn(const nlohmann::json& entity)
330{
331 auto powerState = entity.find("PowerState");
Andrew Jefferyb6209442022-03-24 12:36:20 +1030332 if (powerState == entity.end())
Andrew Jeffery55192932022-03-24 12:29:27 +1030333 {
Andrew Jefferyb6209442022-03-24 12:36:20 +1030334 return false;
Andrew Jeffery55192932022-03-24 12:29:27 +1030335 }
336
Ed Tanous3013fb42022-07-09 08:27:06 -0700337 const auto* ptr = powerState->get_ptr<const std::string*>();
338 if (ptr == nullptr)
Andrew Jefferyb6209442022-03-24 12:36:20 +1030339 {
340 return false;
341 }
342
343 return *ptr == "On" || *ptr == "BiosPost";
Andrew Jeffery55192932022-03-24 12:29:27 +1030344}
345
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030346static void pruneDevice(const nlohmann::json& systemConfiguration,
347 const bool powerOff, const bool scannedPowerOff,
348 const std::string& name, const nlohmann::json& device)
349{
350 if (systemConfiguration.contains(name))
351 {
352 return;
353 }
354
Andrew Jeffery4db38bc2022-03-24 13:42:41 +1030355 if (deviceRequiresPowerOn(device) && (powerOff || scannedPowerOff))
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030356 {
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030357 return;
358 }
359
360 logDeviceRemoved(device);
361}
362
James Feistb1728ca2020-04-30 15:40:55 -0700363void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -0700364 nlohmann::json& systemConfiguration)
365{
366 static bool scannedPowerOff = false;
367 static bool scannedPowerOn = false;
368
James Feistfb00f392019-06-25 14:16:48 -0700369 if (systemConfiguration.empty() || lastJson.empty())
370 {
371 return; // not ready yet
372 }
James Feist1df06a42019-04-11 14:23:04 -0700373 if (scannedPowerOn)
374 {
375 return;
376 }
377
Christopher Meis59ef1e72025-04-16 08:53:25 +0200378 if (!em_utils::isPowerOn() && scannedPowerOff)
James Feist1df06a42019-04-11 14:23:04 -0700379 {
380 return;
381 }
382
James Feistb1728ca2020-04-30 15:40:55 -0700383 timer.expires_after(std::chrono::seconds(10));
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030384 timer.async_wait(
385 [&systemConfiguration](const boost::system::error_code& ec) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400386 if (ec == boost::asio::error::operation_aborted)
387 {
388 return;
389 }
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030390
Christopher Meis59ef1e72025-04-16 08:53:25 +0200391 bool powerOff = !em_utils::isPowerOn();
Patrick Williamsb7077432024-08-16 15:22:21 -0400392 for (const auto& [name, device] : lastJson.items())
393 {
394 pruneDevice(systemConfiguration, powerOff, scannedPowerOff,
395 name, device);
396 }
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030397
Patrick Williamsb7077432024-08-16 15:22:21 -0400398 scannedPowerOff = true;
399 if (!powerOff)
400 {
401 scannedPowerOn = true;
402 }
403 });
James Feist1df06a42019-04-11 14:23:04 -0700404}
405
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030406static void pruneConfiguration(nlohmann::json& systemConfiguration,
407 sdbusplus::asio::object_server& objServer,
408 bool powerOff, const std::string& name,
409 const nlohmann::json& device)
410{
411 if (powerOff && deviceRequiresPowerOn(device))
412 {
413 // power not on yet, don't know if it's there or not
414 return;
415 }
416
Christopher Meis12bea9b2025-04-03 10:14:42 +0200417 auto& ifaces = dbus_interface::getDeviceInterfaces(device);
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030418 for (auto& iface : ifaces)
419 {
420 auto sharedPtr = iface.lock();
421 if (!!sharedPtr)
422 {
423 objServer.remove_interface(sharedPtr);
424 }
425 }
426
427 ifaces.clear();
428 systemConfiguration.erase(name);
Matt Spinler6eb60972023-08-14 16:36:20 -0500429 topology.remove(device["Name"].get<std::string>());
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030430 logDeviceRemoved(device);
431}
432
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030433static void publishNewConfiguration(
434 const size_t& instance, const size_t count,
435 boost::asio::steady_timer& timer, nlohmann::json& systemConfiguration,
436 // Gerrit discussion:
437 // https://gerrit.openbmc-project.xyz/c/openbmc/entity-manager/+/52316/6
438 //
439 // Discord discussion:
440 // https://discord.com/channels/775381525260664832/867820390406422538/958048437729910854
441 //
442 // NOLINTNEXTLINE(performance-unnecessary-value-param)
443 const nlohmann::json newConfiguration,
444 sdbusplus::asio::object_server& objServer)
445{
446 loadOverlays(newConfiguration);
447
Ed Tanous49a888c2023-03-06 13:44:51 -0800448 boost::asio::post(io, [systemConfiguration]() {
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200449 if (!configuration::writeJsonFiles(systemConfiguration))
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030450 {
451 std::cerr << "Error writing json files\n";
452 }
453 });
454
Ed Tanous49a888c2023-03-06 13:44:51 -0800455 boost::asio::post(io, [&instance, count, &timer, newConfiguration,
456 &systemConfiguration, &objServer]() {
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030457 postToDbus(newConfiguration, systemConfiguration, objServer);
458 if (count == instance)
459 {
460 startRemovedTimer(timer, systemConfiguration);
461 }
462 });
463}
464
James Feist8f2710a2018-05-09 17:18:55 -0700465// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -0700466void propertiesChangedCallback(nlohmann::json& systemConfiguration,
467 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -0700468{
James Feist2539ccd2020-05-01 16:15:08 -0700469 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -0700470 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -0700471 static size_t instance = 0;
472 instance++;
473 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -0700474
Anupama B Rbf263982024-01-25 04:42:39 -0600475 timer.expires_after(std::chrono::milliseconds(500));
James Feist8f2710a2018-05-09 17:18:55 -0700476
477 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -0700478 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -0700479 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -0700480 if (ec == boost::asio::error::operation_aborted)
481 {
482 // we were cancelled
483 return;
484 }
Ed Tanous07d467b2021-02-23 14:48:37 -0800485 if (ec)
James Feist8f2710a2018-05-09 17:18:55 -0700486 {
487 std::cerr << "async wait error " << ec << "\n";
488 return;
489 }
490
James Feist2539ccd2020-05-01 16:15:08 -0700491 if (inProgress)
492 {
493 propertiesChangedCallback(systemConfiguration, objServer);
494 return;
495 }
496 inProgress = true;
497
James Feist8f2710a2018-05-09 17:18:55 -0700498 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -0700499 auto missingConfigurations = std::make_shared<nlohmann::json>();
500 *missingConfigurations = systemConfiguration;
501
James Feist8f2710a2018-05-09 17:18:55 -0700502 std::list<nlohmann::json> configurations;
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200503 if (!configuration::loadConfigurations(configurations))
James Feist8f2710a2018-05-09 17:18:55 -0700504 {
Andrew Jefferyf3311792022-03-29 22:09:00 +1030505 std::cerr << "Could not load configurations\n";
James Feist2539ccd2020-05-01 16:15:08 -0700506 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -0700507 return;
508 }
509
Christopher Meis26fbbd52025-03-26 14:55:06 +0100510 auto perfScan = std::make_shared<scan::PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -0700511 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -0700512 objServer,
513 [&systemConfiguration, &objServer, count, oldConfiguration,
514 missingConfigurations]() {
Patrick Williamsb7077432024-08-16 15:22:21 -0400515 // this is something that since ac has been applied to the bmc
516 // we saw, and we no longer see it
Christopher Meis59ef1e72025-04-16 08:53:25 +0200517 bool powerOff = !em_utils::isPowerOn();
Patrick Williamsb7077432024-08-16 15:22:21 -0400518 for (const auto& [name, device] :
519 missingConfigurations->items())
520 {
521 pruneConfiguration(systemConfiguration, objServer, powerOff,
522 name, device);
523 }
James Feist899e17f2019-09-13 11:46:29 -0700524
Patrick Williamsb7077432024-08-16 15:22:21 -0400525 nlohmann::json newConfiguration = systemConfiguration;
Andrew Jeffery7c4cbbe2022-03-24 15:27:10 +1030526
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200527 configuration::deriveNewConfiguration(oldConfiguration,
528 newConfiguration);
Andrew Jeffery7c4cbbe2022-03-24 15:27:10 +1030529
Patrick Williamsb7077432024-08-16 15:22:21 -0400530 for (const auto& [_, device] : newConfiguration.items())
531 {
532 logDeviceAdded(device);
533 }
James Feist899e17f2019-09-13 11:46:29 -0700534
Patrick Williamsb7077432024-08-16 15:22:21 -0400535 inProgress = false;
James Feist2539ccd2020-05-01 16:15:08 -0700536
Patrick Williamsb7077432024-08-16 15:22:21 -0400537 boost::asio::post(
538 io, std::bind_front(
539 publishNewConfiguration, std::ref(instance), count,
540 std::ref(timer), std::ref(systemConfiguration),
541 newConfiguration, std::ref(objServer)));
542 });
James Feist8f2710a2018-05-09 17:18:55 -0700543 perfScan->run();
544 });
James Feist75fdeeb2018-02-20 14:26:16 -0800545}
546
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600547// Check if InterfacesAdded payload contains an iface that needs probing.
Patrick Williamsb7077432024-08-16 15:22:21 -0400548static bool iaContainsProbeInterface(
549 sdbusplus::message_t& msg, const std::set<std::string>& probeInterfaces)
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600550{
551 sdbusplus::message::object_path path;
552 DBusObject interfaces;
553 std::set<std::string> interfaceSet;
554 std::set<std::string> intersect;
555
556 msg.read(path, interfaces);
557
558 std::for_each(interfaces.begin(), interfaces.end(),
559 [&interfaceSet](const auto& iface) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400560 interfaceSet.insert(iface.first);
561 });
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600562
563 std::set_intersection(interfaceSet.begin(), interfaceSet.end(),
564 probeInterfaces.begin(), probeInterfaces.end(),
565 std::inserter(intersect, intersect.end()));
566 return !intersect.empty();
567}
568
569// Check if InterfacesRemoved payload contains an iface that needs probing.
Patrick Williamsb7077432024-08-16 15:22:21 -0400570static bool irContainsProbeInterface(
571 sdbusplus::message_t& msg, const std::set<std::string>& probeInterfaces)
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600572{
573 sdbusplus::message::object_path path;
574 std::set<std::string> interfaces;
575 std::set<std::string> intersect;
576
577 msg.read(path, interfaces);
578
579 std::set_intersection(interfaces.begin(), interfaces.end(),
580 probeInterfaces.begin(), probeInterfaces.end(),
581 std::inserter(intersect, intersect.end()));
582 return !intersect.empty();
583}
584
James Feist98132792019-07-09 13:29:09 -0700585int main()
James Feist75fdeeb2018-02-20 14:26:16 -0800586{
587 // setup connection to dbus
Ed Tanous07d467b2021-02-23 14:48:37 -0800588 systemBus = std::make_shared<sdbusplus::asio::connection>(io);
589 systemBus->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -0800590
Nan Zhoua3315672022-09-20 19:48:14 +0000591 // The EntityManager object itself doesn't expose any properties.
592 // No need to set up ObjectManager for the |EntityManager| object.
593 sdbusplus::asio::object_server objServer(systemBus, /*skipManager=*/true);
594
595 // All other objects that EntityManager currently support are under the
596 // inventory subtree.
597 // See the discussion at
598 // https://discord.com/channels/775381525260664832/1018929092009144380
599 objServer.add_manager("/xyz/openbmc_project/inventory");
James Feistfd1264a2018-05-03 12:10:00 -0700600
James Feist8f2710a2018-05-09 17:18:55 -0700601 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
602 objServer.add_interface("/xyz/openbmc_project/EntityManager",
603 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -0700604
James Feist4131aea2018-03-09 09:47:30 -0800605 // to keep reference to the match / filter objects so they don't get
606 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -0700607
608 nlohmann::json systemConfiguration = nlohmann::json::object();
609
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200610 std::set<std::string> probeInterfaces = configuration::getProbeInterfaces();
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600611
Brad Bishopc76af0f2020-12-04 13:50:23 -0500612 // We need a poke from DBus for static providers that create all their
613 // objects prior to claiming a well-known name, and thus don't emit any
614 // org.freedesktop.DBus.Properties signals. Similarly if a process exits
615 // for any reason, expected or otherwise, we'll need a poke to remove
616 // entities from DBus.
Patrick Williams2af39222022-07-22 19:26:56 -0500617 sdbusplus::bus::match_t nameOwnerChangedMatch(
618 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishopc76af0f2020-12-04 13:50:23 -0500619 sdbusplus::bus::match::rules::nameOwnerChanged(),
Patrick Williams7b8786f2022-10-10 10:23:37 -0500620 [&](sdbusplus::message_t& m) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400621 auto [name, oldOwner,
622 newOwner] = m.unpack<std::string, std::string, std::string>();
Patrick Williams7b8786f2022-10-10 10:23:37 -0500623
Patrick Williamsb7077432024-08-16 15:22:21 -0400624 if (name.starts_with(':'))
625 {
626 // We should do nothing with unique-name connections.
627 return;
628 }
Patrick Williams7b8786f2022-10-10 10:23:37 -0500629
Patrick Williamsb7077432024-08-16 15:22:21 -0400630 propertiesChangedCallback(systemConfiguration, objServer);
631 });
Brad Bishop10a8c5f2020-12-07 21:40:07 -0500632 // We also need a poke from DBus when new interfaces are created or
633 // destroyed.
Patrick Williams2af39222022-07-22 19:26:56 -0500634 sdbusplus::bus::match_t interfacesAddedMatch(
635 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -0500636 sdbusplus::bus::match::rules::interfacesAdded(),
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600637 [&](sdbusplus::message_t& msg) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400638 if (iaContainsProbeInterface(msg, probeInterfaces))
639 {
640 propertiesChangedCallback(systemConfiguration, objServer);
641 }
642 });
Patrick Williams2af39222022-07-22 19:26:56 -0500643 sdbusplus::bus::match_t interfacesRemovedMatch(
644 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -0500645 sdbusplus::bus::match::rules::interfacesRemoved(),
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600646 [&](sdbusplus::message_t& msg) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400647 if (irContainsProbeInterface(msg, probeInterfaces))
648 {
649 propertiesChangedCallback(systemConfiguration, objServer);
650 }
651 });
Brad Bishopc76af0f2020-12-04 13:50:23 -0500652
Ed Tanous49a888c2023-03-06 13:44:51 -0800653 boost::asio::post(io, [&]() {
654 propertiesChangedCallback(systemConfiguration, objServer);
655 });
James Feist4131aea2018-03-09 09:47:30 -0800656
James Feistfd1264a2018-05-03 12:10:00 -0700657 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -0700658 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800659 });
Christopher Meis12bea9b2025-04-03 10:14:42 +0200660 dbus_interface::tryIfaceInitialize(entityIface);
James Feist8f2710a2018-05-09 17:18:55 -0700661
Christopher Meis59ef1e72025-04-16 08:53:25 +0200662 if (em_utils::fwVersionIsSame())
James Feist1df06a42019-04-11 14:23:04 -0700663 {
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200664 if (std::filesystem::is_regular_file(
665 configuration::currentConfiguration))
James Feist1df06a42019-04-11 14:23:04 -0700666 {
667 // this file could just be deleted, but it's nice for debug
668 std::filesystem::create_directory(tempConfigDir);
669 std::filesystem::remove(lastConfiguration);
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200670 std::filesystem::copy(configuration::currentConfiguration,
671 lastConfiguration);
672 std::filesystem::remove(configuration::currentConfiguration);
James Feist1df06a42019-04-11 14:23:04 -0700673
674 std::ifstream jsonStream(lastConfiguration);
675 if (jsonStream.good())
676 {
677 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
678 if (data.is_discarded())
679 {
Patrick Williamsb7077432024-08-16 15:22:21 -0400680 std::cerr
681 << "syntax error in " << lastConfiguration << "\n";
James Feist1df06a42019-04-11 14:23:04 -0700682 }
683 else
684 {
685 lastJson = std::move(data);
686 }
687 }
688 else
689 {
690 std::cerr << "unable to open " << lastConfiguration << "\n";
691 }
692 }
693 }
694 else
695 {
696 // not an error, just logging at this level to make it in the journal
697 std::cerr << "Clearing previous configuration\n";
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200698 std::filesystem::remove(configuration::currentConfiguration);
James Feist1df06a42019-04-11 14:23:04 -0700699 }
700
701 // some boards only show up after power is on, we want to not say they are
702 // removed until the same state happens
Christopher Meis59ef1e72025-04-16 08:53:25 +0200703 em_utils::setupPowerMatch(systemBus);
James Feist1df06a42019-04-11 14:23:04 -0700704
James Feist1b2e2242018-01-30 13:45:19 -0800705 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -0800706
707 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -0800708}