blob: 5aa538057d2481b97b5b5382dc27c055833ee847 [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"
James Feist481c5d52019-08-13 14:40:40 -070027
James Feist11be6672018-04-06 14:05:32 -070028#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070029#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080030#include <boost/algorithm/string/predicate.hpp>
31#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070032#include <boost/algorithm/string/split.hpp>
James Feist02d2b932020-02-06 16:28:48 -080033#include <boost/asio/io_context.hpp>
Ed Tanous49a888c2023-03-06 13:44:51 -080034#include <boost/asio/post.hpp>
James Feistb1728ca2020-04-30 15:40:55 -070035#include <boost/asio/steady_timer.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080036#include <boost/container/flat_map.hpp>
37#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070038#include <boost/range/iterator_range.hpp>
James Feist8c505da2020-05-28 10:06:33 -070039#include <nlohmann/json.hpp>
40#include <sdbusplus/asio/connection.hpp>
41#include <sdbusplus/asio/object_server.hpp>
42
Igor Kononenko9fd87e52020-10-06 01:18:17 +030043#include <charconv>
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 Feista465ccc2019-02-08 12:51:01 -080050#include <variant>
James Feist1df06a42019-04-11 14:23:04 -070051constexpr const char* tempConfigDir = "/tmp/configuration/";
52constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
James Feistf1b14142019-04-10 15:22:09 -070053
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020054static constexpr std::array<const char*, 6> settableInterfaces = {
55 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist3cb5fec2018-01-23 14:41:51 -080056
Ed Tanousfc171422024-04-04 17:18:16 -070057// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
James Feistd58879a2019-09-11 11:26:07 -070058
James Feist3cb5fec2018-01-23 14:41:51 -080059// todo: pass this through nicer
Ed Tanous07d467b2021-02-23 14:48:37 -080060std::shared_ptr<sdbusplus::asio::connection> systemBus;
Andrew Jeffery47af65a2021-12-01 14:16:31 +103061nlohmann::json lastJson;
Matt Spinler6eb60972023-08-14 16:36:20 -050062Topology topology;
James Feist3cb5fec2018-01-23 14:41:51 -080063
James Feist02d2b932020-02-06 16:28:48 -080064boost::asio::io_context io;
Ed Tanousfc171422024-04-04 17:18:16 -070065// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
James Feist02d2b932020-02-06 16:28:48 -080066
Ed Tanous07d467b2021-02-23 14:48:37 -080067const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
68const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -080069
James Feista465ccc2019-02-08 12:51:01 -080070sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -070071{
72 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
73 interface) != settableInterfaces.end()
74 ? sdbusplus::asio::PropertyPermission::readWrite
75 : sdbusplus::asio::PropertyPermission::readOnly;
76}
77
James Feista465ccc2019-02-08 12:51:01 -080078void postToDbus(const nlohmann::json& newConfiguration,
79 nlohmann::json& systemConfiguration,
80 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -080081
James Feist1b2e2242018-01-30 13:45:19 -080082{
Matt Spinler6eb60972023-08-14 16:36:20 -050083 std::map<std::string, std::string> newBoards; // path -> name
Benjamin Fairca2eb042022-09-13 06:40:42 +000084
James Feist97a63f12018-05-17 13:50:57 -070085 // iterate through boards
Patrick Williams2594d362022-09-28 06:46:24 -050086 for (const auto& [boardId, boardConfig] : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -080087 {
Matt Spinler3d1909a2023-08-10 16:39:44 -050088 std::string boardName = boardConfig["Name"];
89 std::string boardNameOrig = boardConfig["Name"];
Andrew Jeffery13132df2022-03-25 13:29:41 +103090 std::string jsonPointerPath = "/" + boardId;
James Feist97a63f12018-05-17 13:50:57 -070091 // loop through newConfiguration, but use values from system
92 // configuration to be able to modify via dbus later
Andrew Jeffery13132df2022-03-25 13:29:41 +103093 auto boardValues = systemConfiguration[boardId];
James Feistd63d18a2018-07-19 15:23:45 -070094 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -080095 std::string boardType;
96 if (findBoardType != boardValues.end() &&
97 findBoardType->type() == nlohmann::json::value_t::string)
98 {
99 boardType = findBoardType->get<std::string>();
100 std::regex_replace(boardType.begin(), boardType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800101 boardType.end(), illegalDbusMemberRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800102 }
103 else
104 {
Matt Spinler3d1909a2023-08-10 16:39:44 -0500105 std::cerr << "Unable to find type for " << boardName
James Feist1b2e2242018-01-30 13:45:19 -0800106 << " reverting to Chassis.\n";
107 boardType = "Chassis";
108 }
James Feist11be6672018-04-06 14:05:32 -0700109 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800110
Matt Spinler3d1909a2023-08-10 16:39:44 -0500111 std::regex_replace(boardName.begin(), boardName.begin(),
112 boardName.end(), illegalDbusMemberRegex, "_");
113 std::string boardPath = "/xyz/openbmc_project/inventory/system/";
114 boardPath += boardtypeLower;
115 boardPath += "/";
116 boardPath += boardName;
James Feist1b2e2242018-01-30 13:45:19 -0800117
James Feistd58879a2019-09-11 11:26:07 -0700118 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
Christopher Meis12bea9b2025-04-03 10:14:42 +0200119 dbus_interface::createInterface(
120 objServer, boardPath, "xyz.openbmc_project.Inventory.Item",
121 boardName);
James Feist68500ff2018-08-08 15:40:42 -0700122
James Feistd58879a2019-09-11 11:26:07 -0700123 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
Christopher Meis12bea9b2025-04-03 10:14:42 +0200124 dbus_interface::createInterface(
125 objServer, boardPath,
126 "xyz.openbmc_project.Inventory.Item." + boardType,
127 boardNameOrig);
James Feist11be6672018-04-06 14:05:32 -0700128
Christopher Meis12bea9b2025-04-03 10:14:42 +0200129 dbus_interface::createAddObjectMethod(
130 jsonPointerPath, boardPath, systemConfiguration, objServer,
131 boardNameOrig);
James Feist68500ff2018-08-08 15:40:42 -0700132
Christopher Meis12bea9b2025-04-03 10:14:42 +0200133 dbus_interface::populateInterfaceFromJson(
134 systemConfiguration, jsonPointerPath, boardIface, boardValues,
135 objServer);
James Feist97a63f12018-05-17 13:50:57 -0700136 jsonPointerPath += "/";
137 // iterate through board properties
Patrick Williams2594d362022-09-28 06:46:24 -0500138 for (const auto& [propName, propValue] : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700139 {
Andrew Jefferya96950d2022-03-25 13:32:46 +1030140 if (propValue.type() == nlohmann::json::value_t::object)
James Feist11be6672018-04-06 14:05:32 -0700141 {
James Feistd58879a2019-09-11 11:26:07 -0700142 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
Christopher Meis12bea9b2025-04-03 10:14:42 +0200143 dbus_interface::createInterface(objServer, boardPath,
144 propName, boardNameOrig);
James Feistd58879a2019-09-11 11:26:07 -0700145
Christopher Meis12bea9b2025-04-03 10:14:42 +0200146 dbus_interface::populateInterfaceFromJson(
147 systemConfiguration, jsonPointerPath + propName, iface,
148 propValue, objServer);
James Feist11be6672018-04-06 14:05:32 -0700149 }
150 }
James Feist97a63f12018-05-17 13:50:57 -0700151
James Feist1e3e6982018-08-03 16:09:28 -0700152 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800153 if (exposes == boardValues.end())
154 {
155 continue;
156 }
James Feist97a63f12018-05-17 13:50:57 -0700157 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700158 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700159
160 // store the board level pointer so we can modify it on the way down
161 std::string jsonPointerPathBoard = jsonPointerPath;
162 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -0800163 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -0800164 {
James Feist97a63f12018-05-17 13:50:57 -0700165 exposesIndex++;
166 jsonPointerPath = jsonPointerPathBoard;
167 jsonPointerPath += std::to_string(exposesIndex);
168
James Feistd63d18a2018-07-19 15:23:45 -0700169 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800170 if (findName == item.end())
171 {
172 std::cerr << "cannot find name in field " << item << "\n";
173 continue;
174 }
James Feist1e3e6982018-08-03 16:09:28 -0700175 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800176 // if status is not found it is assumed to be status = 'okay'
177 if (findStatus != item.end())
178 {
179 if (*findStatus == "disabled")
180 {
181 continue;
182 }
183 }
James Feistd63d18a2018-07-19 15:23:45 -0700184 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800185 std::string itemType;
186 if (findType != item.end())
187 {
188 itemType = findType->get<std::string>();
189 std::regex_replace(itemType.begin(), itemType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800190 itemType.end(), illegalDbusPathRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800191 }
192 else
193 {
194 itemType = "unknown";
195 }
196 std::string itemName = findName->get<std::string>();
197 std::regex_replace(itemName.begin(), itemName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800198 itemName.end(), illegalDbusMemberRegex, "_");
Matt Spinler3d1909a2023-08-10 16:39:44 -0500199 std::string ifacePath = boardPath;
Ed Tanous07d467b2021-02-23 14:48:37 -0800200 ifacePath += "/";
201 ifacePath += itemName;
James Feistc6248a52018-08-14 10:09:45 -0700202
Sui Chen74ebe592022-09-13 10:22:03 -0700203 if (itemType == "BMC")
204 {
205 std::shared_ptr<sdbusplus::asio::dbus_interface> bmcIface =
Christopher Meis12bea9b2025-04-03 10:14:42 +0200206 dbus_interface::createInterface(
207 objServer, ifacePath,
208 "xyz.openbmc_project.Inventory.Item.Bmc",
209 boardNameOrig);
210 dbus_interface::populateInterfaceFromJson(
211 systemConfiguration, jsonPointerPath, bmcIface, item,
212 objServer, getPermission(itemType));
Sui Chen74ebe592022-09-13 10:22:03 -0700213 }
Edward Leeeb587b42023-03-08 18:59:04 +0000214 else if (itemType == "System")
215 {
216 std::shared_ptr<sdbusplus::asio::dbus_interface> systemIface =
Christopher Meis12bea9b2025-04-03 10:14:42 +0200217 dbus_interface::createInterface(
218 objServer, ifacePath,
219 "xyz.openbmc_project.Inventory.Item.System",
220 boardNameOrig);
221 dbus_interface::populateInterfaceFromJson(
222 systemConfiguration, jsonPointerPath, systemIface, item,
223 objServer, getPermission(itemType));
Edward Leeeb587b42023-03-08 18:59:04 +0000224 }
Sui Chen74ebe592022-09-13 10:22:03 -0700225
Patrick Williams2594d362022-09-28 06:46:24 -0500226 for (const auto& [name, config] : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800227 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030228 jsonPointerPath = jsonPointerPathBoard;
229 jsonPointerPath.append(std::to_string(exposesIndex))
230 .append("/")
231 .append(name);
232 if (config.type() == nlohmann::json::value_t::object)
James Feist1b2e2242018-01-30 13:45:19 -0800233 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030234 std::string ifaceName =
235 "xyz.openbmc_project.Configuration.";
236 ifaceName.append(itemType).append(".").append(name);
James Feist97a63f12018-05-17 13:50:57 -0700237
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030238 std::shared_ptr<sdbusplus::asio::dbus_interface>
Christopher Meis12bea9b2025-04-03 10:14:42 +0200239 objectIface = dbus_interface::createInterface(
240 objServer, ifacePath, ifaceName, boardNameOrig);
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030241
Christopher Meis12bea9b2025-04-03 10:14:42 +0200242 dbus_interface::populateInterfaceFromJson(
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030243 systemConfiguration, jsonPointerPath, objectIface,
244 config, objServer, getPermission(name));
James Feist1b2e2242018-01-30 13:45:19 -0800245 }
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030246 else if (config.type() == nlohmann::json::value_t::array)
James Feist1b2e2242018-01-30 13:45:19 -0800247 {
248 size_t index = 0;
Ed Tanous3013fb42022-07-09 08:27:06 -0700249 if (config.empty())
James Feist1b2e2242018-01-30 13:45:19 -0800250 {
James Feist8f2710a2018-05-09 17:18:55 -0700251 continue;
252 }
253 bool isLegal = true;
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030254 auto type = config[0].type();
James Feist8f2710a2018-05-09 17:18:55 -0700255 if (type != nlohmann::json::value_t::object)
256 {
257 continue;
258 }
259
260 // verify legal json
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030261 for (const auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700262 {
263 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800264 {
James Feist8f2710a2018-05-09 17:18:55 -0700265 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800266 break;
267 }
James Feist8f2710a2018-05-09 17:18:55 -0700268 }
269 if (!isLegal)
270 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030271 std::cerr << "dbus format error" << config << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700272 break;
273 }
274
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030275 for (auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700276 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030277 std::string ifaceName =
278 "xyz.openbmc_project.Configuration.";
279 ifaceName.append(itemType).append(".").append(name);
280 ifaceName.append(std::to_string(index));
James Feist97a63f12018-05-17 13:50:57 -0700281
James Feistd58879a2019-09-11 11:26:07 -0700282 std::shared_ptr<sdbusplus::asio::dbus_interface>
Christopher Meis12bea9b2025-04-03 10:14:42 +0200283 objectIface = dbus_interface::createInterface(
Matt Spinler3d1909a2023-08-10 16:39:44 -0500284 objServer, ifacePath, ifaceName, boardNameOrig);
James Feistd58879a2019-09-11 11:26:07 -0700285
Christopher Meis12bea9b2025-04-03 10:14:42 +0200286 dbus_interface::populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -0700287 systemConfiguration,
288 jsonPointerPath + "/" + std::to_string(index),
289 objectIface, arrayItem, objServer,
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030290 getPermission(name));
James Feistbb43d022018-06-12 15:44:33 -0700291 index++;
James Feist1b2e2242018-01-30 13:45:19 -0800292 }
293 }
294 }
Benjamin Fairca2eb042022-09-13 06:40:42 +0000295
George Liu5c1a61a2024-10-24 18:02:29 +0800296 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
Christopher Meis12bea9b2025-04-03 10:14:42 +0200297 dbus_interface::createInterface(
298 objServer, ifacePath,
299 "xyz.openbmc_project.Configuration." + itemType,
300 boardNameOrig);
George Liu5c1a61a2024-10-24 18:02:29 +0800301
Christopher Meis12bea9b2025-04-03 10:14:42 +0200302 dbus_interface::populateInterfaceFromJson(
303 systemConfiguration, jsonPointerPath, itemIface, item,
304 objServer, getPermission(itemType));
George Liu5c1a61a2024-10-24 18:02:29 +0800305
Matt Spinler6eb60972023-08-14 16:36:20 -0500306 topology.addBoard(boardPath, boardType, boardNameOrig, item);
James Feist1b2e2242018-01-30 13:45:19 -0800307 }
Matt Spinler6eb60972023-08-14 16:36:20 -0500308
309 newBoards.emplace(boardPath, boardNameOrig);
James Feist1b2e2242018-01-30 13:45:19 -0800310 }
Benjamin Fairca2eb042022-09-13 06:40:42 +0000311
Matt Spinler6eb60972023-08-14 16:36:20 -0500312 for (const auto& [assocPath, assocPropValue] :
313 topology.getAssocs(newBoards))
Benjamin Fairca2eb042022-09-13 06:40:42 +0000314 {
Matt Spinler6eb60972023-08-14 16:36:20 -0500315 auto findBoard = newBoards.find(assocPath);
316 if (findBoard == newBoards.end())
317 {
318 continue;
319 }
Benjamin Fairca2eb042022-09-13 06:40:42 +0000320
Christopher Meis12bea9b2025-04-03 10:14:42 +0200321 auto ifacePtr = dbus_interface::createInterface(
Matt Spinler6eb60972023-08-14 16:36:20 -0500322 objServer, assocPath, "xyz.openbmc_project.Association.Definitions",
323 findBoard->second);
324
325 ifacePtr->register_property("Associations", assocPropValue);
Christopher Meis12bea9b2025-04-03 10:14:42 +0200326 dbus_interface::tryIfaceInitialize(ifacePtr);
Benjamin Fairca2eb042022-09-13 06:40:42 +0000327 }
James Feist1b2e2242018-01-30 13:45:19 -0800328}
329
Andrew Jeffery55192932022-03-24 12:29:27 +1030330static bool deviceRequiresPowerOn(const nlohmann::json& entity)
331{
332 auto powerState = entity.find("PowerState");
Andrew Jefferyb6209442022-03-24 12:36:20 +1030333 if (powerState == entity.end())
Andrew Jeffery55192932022-03-24 12:29:27 +1030334 {
Andrew Jefferyb6209442022-03-24 12:36:20 +1030335 return false;
Andrew Jeffery55192932022-03-24 12:29:27 +1030336 }
337
Ed Tanous3013fb42022-07-09 08:27:06 -0700338 const auto* ptr = powerState->get_ptr<const std::string*>();
339 if (ptr == nullptr)
Andrew Jefferyb6209442022-03-24 12:36:20 +1030340 {
341 return false;
342 }
343
344 return *ptr == "On" || *ptr == "BiosPost";
Andrew Jeffery55192932022-03-24 12:29:27 +1030345}
346
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030347static void pruneDevice(const nlohmann::json& systemConfiguration,
348 const bool powerOff, const bool scannedPowerOff,
349 const std::string& name, const nlohmann::json& device)
350{
351 if (systemConfiguration.contains(name))
352 {
353 return;
354 }
355
Andrew Jeffery4db38bc2022-03-24 13:42:41 +1030356 if (deviceRequiresPowerOn(device) && (powerOff || scannedPowerOff))
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030357 {
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030358 return;
359 }
360
361 logDeviceRemoved(device);
362}
363
James Feistb1728ca2020-04-30 15:40:55 -0700364void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -0700365 nlohmann::json& systemConfiguration)
366{
367 static bool scannedPowerOff = false;
368 static bool scannedPowerOn = false;
369
James Feistfb00f392019-06-25 14:16:48 -0700370 if (systemConfiguration.empty() || lastJson.empty())
371 {
372 return; // not ready yet
373 }
James Feist1df06a42019-04-11 14:23:04 -0700374 if (scannedPowerOn)
375 {
376 return;
377 }
378
379 if (!isPowerOn() && scannedPowerOff)
380 {
381 return;
382 }
383
James Feistb1728ca2020-04-30 15:40:55 -0700384 timer.expires_after(std::chrono::seconds(10));
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030385 timer.async_wait(
386 [&systemConfiguration](const boost::system::error_code& ec) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400387 if (ec == boost::asio::error::operation_aborted)
388 {
389 return;
390 }
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030391
Patrick Williamsb7077432024-08-16 15:22:21 -0400392 bool powerOff = !isPowerOn();
393 for (const auto& [name, device] : lastJson.items())
394 {
395 pruneDevice(systemConfiguration, powerOff, scannedPowerOff,
396 name, device);
397 }
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030398
Patrick Williamsb7077432024-08-16 15:22:21 -0400399 scannedPowerOff = true;
400 if (!powerOff)
401 {
402 scannedPowerOn = true;
403 }
404 });
James Feist1df06a42019-04-11 14:23:04 -0700405}
406
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030407static void pruneConfiguration(nlohmann::json& systemConfiguration,
408 sdbusplus::asio::object_server& objServer,
409 bool powerOff, const std::string& name,
410 const nlohmann::json& device)
411{
412 if (powerOff && deviceRequiresPowerOn(device))
413 {
414 // power not on yet, don't know if it's there or not
415 return;
416 }
417
Christopher Meis12bea9b2025-04-03 10:14:42 +0200418 auto& ifaces = dbus_interface::getDeviceInterfaces(device);
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030419 for (auto& iface : ifaces)
420 {
421 auto sharedPtr = iface.lock();
422 if (!!sharedPtr)
423 {
424 objServer.remove_interface(sharedPtr);
425 }
426 }
427
428 ifaces.clear();
429 systemConfiguration.erase(name);
Matt Spinler6eb60972023-08-14 16:36:20 -0500430 topology.remove(device["Name"].get<std::string>());
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030431 logDeviceRemoved(device);
432}
433
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030434static void publishNewConfiguration(
435 const size_t& instance, const size_t count,
436 boost::asio::steady_timer& timer, nlohmann::json& systemConfiguration,
437 // Gerrit discussion:
438 // https://gerrit.openbmc-project.xyz/c/openbmc/entity-manager/+/52316/6
439 //
440 // Discord discussion:
441 // https://discord.com/channels/775381525260664832/867820390406422538/958048437729910854
442 //
443 // NOLINTNEXTLINE(performance-unnecessary-value-param)
444 const nlohmann::json newConfiguration,
445 sdbusplus::asio::object_server& objServer)
446{
447 loadOverlays(newConfiguration);
448
Ed Tanous49a888c2023-03-06 13:44:51 -0800449 boost::asio::post(io, [systemConfiguration]() {
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200450 if (!configuration::writeJsonFiles(systemConfiguration))
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030451 {
452 std::cerr << "Error writing json files\n";
453 }
454 });
455
Ed Tanous49a888c2023-03-06 13:44:51 -0800456 boost::asio::post(io, [&instance, count, &timer, newConfiguration,
457 &systemConfiguration, &objServer]() {
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030458 postToDbus(newConfiguration, systemConfiguration, objServer);
459 if (count == instance)
460 {
461 startRemovedTimer(timer, systemConfiguration);
462 }
463 });
464}
465
James Feist8f2710a2018-05-09 17:18:55 -0700466// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -0700467void propertiesChangedCallback(nlohmann::json& systemConfiguration,
468 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -0700469{
James Feist2539ccd2020-05-01 16:15:08 -0700470 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -0700471 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -0700472 static size_t instance = 0;
473 instance++;
474 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -0700475
Anupama B Rbf263982024-01-25 04:42:39 -0600476 timer.expires_after(std::chrono::milliseconds(500));
James Feist8f2710a2018-05-09 17:18:55 -0700477
478 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -0700479 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -0700480 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -0700481 if (ec == boost::asio::error::operation_aborted)
482 {
483 // we were cancelled
484 return;
485 }
Ed Tanous07d467b2021-02-23 14:48:37 -0800486 if (ec)
James Feist8f2710a2018-05-09 17:18:55 -0700487 {
488 std::cerr << "async wait error " << ec << "\n";
489 return;
490 }
491
James Feist2539ccd2020-05-01 16:15:08 -0700492 if (inProgress)
493 {
494 propertiesChangedCallback(systemConfiguration, objServer);
495 return;
496 }
497 inProgress = true;
498
James Feist8f2710a2018-05-09 17:18:55 -0700499 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -0700500 auto missingConfigurations = std::make_shared<nlohmann::json>();
501 *missingConfigurations = systemConfiguration;
502
James Feist8f2710a2018-05-09 17:18:55 -0700503 std::list<nlohmann::json> configurations;
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200504 if (!configuration::loadConfigurations(configurations))
James Feist8f2710a2018-05-09 17:18:55 -0700505 {
Andrew Jefferyf3311792022-03-29 22:09:00 +1030506 std::cerr << "Could not load configurations\n";
James Feist2539ccd2020-05-01 16:15:08 -0700507 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -0700508 return;
509 }
510
Christopher Meis26fbbd52025-03-26 14:55:06 +0100511 auto perfScan = std::make_shared<scan::PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -0700512 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -0700513 objServer,
514 [&systemConfiguration, &objServer, count, oldConfiguration,
515 missingConfigurations]() {
Patrick Williamsb7077432024-08-16 15:22:21 -0400516 // this is something that since ac has been applied to the bmc
517 // we saw, and we no longer see it
518 bool powerOff = !isPowerOn();
519 for (const auto& [name, device] :
520 missingConfigurations->items())
521 {
522 pruneConfiguration(systemConfiguration, objServer, powerOff,
523 name, device);
524 }
James Feist899e17f2019-09-13 11:46:29 -0700525
Patrick Williamsb7077432024-08-16 15:22:21 -0400526 nlohmann::json newConfiguration = systemConfiguration;
Andrew Jeffery7c4cbbe2022-03-24 15:27:10 +1030527
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200528 configuration::deriveNewConfiguration(oldConfiguration,
529 newConfiguration);
Andrew Jeffery7c4cbbe2022-03-24 15:27:10 +1030530
Patrick Williamsb7077432024-08-16 15:22:21 -0400531 for (const auto& [_, device] : newConfiguration.items())
532 {
533 logDeviceAdded(device);
534 }
James Feist899e17f2019-09-13 11:46:29 -0700535
Patrick Williamsb7077432024-08-16 15:22:21 -0400536 inProgress = false;
James Feist2539ccd2020-05-01 16:15:08 -0700537
Patrick Williamsb7077432024-08-16 15:22:21 -0400538 boost::asio::post(
539 io, std::bind_front(
540 publishNewConfiguration, std::ref(instance), count,
541 std::ref(timer), std::ref(systemConfiguration),
542 newConfiguration, std::ref(objServer)));
543 });
James Feist8f2710a2018-05-09 17:18:55 -0700544 perfScan->run();
545 });
James Feist75fdeeb2018-02-20 14:26:16 -0800546}
547
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600548// Check if InterfacesAdded payload contains an iface that needs probing.
Patrick Williamsb7077432024-08-16 15:22:21 -0400549static bool iaContainsProbeInterface(
550 sdbusplus::message_t& msg, const std::set<std::string>& probeInterfaces)
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600551{
552 sdbusplus::message::object_path path;
553 DBusObject interfaces;
554 std::set<std::string> interfaceSet;
555 std::set<std::string> intersect;
556
557 msg.read(path, interfaces);
558
559 std::for_each(interfaces.begin(), interfaces.end(),
560 [&interfaceSet](const auto& iface) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400561 interfaceSet.insert(iface.first);
562 });
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600563
564 std::set_intersection(interfaceSet.begin(), interfaceSet.end(),
565 probeInterfaces.begin(), probeInterfaces.end(),
566 std::inserter(intersect, intersect.end()));
567 return !intersect.empty();
568}
569
570// Check if InterfacesRemoved payload contains an iface that needs probing.
Patrick Williamsb7077432024-08-16 15:22:21 -0400571static bool irContainsProbeInterface(
572 sdbusplus::message_t& msg, const std::set<std::string>& probeInterfaces)
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600573{
574 sdbusplus::message::object_path path;
575 std::set<std::string> interfaces;
576 std::set<std::string> intersect;
577
578 msg.read(path, interfaces);
579
580 std::set_intersection(interfaces.begin(), interfaces.end(),
581 probeInterfaces.begin(), probeInterfaces.end(),
582 std::inserter(intersect, intersect.end()));
583 return !intersect.empty();
584}
585
James Feist98132792019-07-09 13:29:09 -0700586int main()
James Feist75fdeeb2018-02-20 14:26:16 -0800587{
588 // setup connection to dbus
Ed Tanous07d467b2021-02-23 14:48:37 -0800589 systemBus = std::make_shared<sdbusplus::asio::connection>(io);
590 systemBus->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -0800591
Nan Zhoua3315672022-09-20 19:48:14 +0000592 // The EntityManager object itself doesn't expose any properties.
593 // No need to set up ObjectManager for the |EntityManager| object.
594 sdbusplus::asio::object_server objServer(systemBus, /*skipManager=*/true);
595
596 // All other objects that EntityManager currently support are under the
597 // inventory subtree.
598 // See the discussion at
599 // https://discord.com/channels/775381525260664832/1018929092009144380
600 objServer.add_manager("/xyz/openbmc_project/inventory");
James Feistfd1264a2018-05-03 12:10:00 -0700601
James Feist8f2710a2018-05-09 17:18:55 -0700602 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
603 objServer.add_interface("/xyz/openbmc_project/EntityManager",
604 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -0700605
James Feist4131aea2018-03-09 09:47:30 -0800606 // to keep reference to the match / filter objects so they don't get
607 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -0700608
609 nlohmann::json systemConfiguration = nlohmann::json::object();
610
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200611 std::set<std::string> probeInterfaces = configuration::getProbeInterfaces();
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600612
Brad Bishopc76af0f2020-12-04 13:50:23 -0500613 // We need a poke from DBus for static providers that create all their
614 // objects prior to claiming a well-known name, and thus don't emit any
615 // org.freedesktop.DBus.Properties signals. Similarly if a process exits
616 // for any reason, expected or otherwise, we'll need a poke to remove
617 // entities from DBus.
Patrick Williams2af39222022-07-22 19:26:56 -0500618 sdbusplus::bus::match_t nameOwnerChangedMatch(
619 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishopc76af0f2020-12-04 13:50:23 -0500620 sdbusplus::bus::match::rules::nameOwnerChanged(),
Patrick Williams7b8786f2022-10-10 10:23:37 -0500621 [&](sdbusplus::message_t& m) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400622 auto [name, oldOwner,
623 newOwner] = m.unpack<std::string, std::string, std::string>();
Patrick Williams7b8786f2022-10-10 10:23:37 -0500624
Patrick Williamsb7077432024-08-16 15:22:21 -0400625 if (name.starts_with(':'))
626 {
627 // We should do nothing with unique-name connections.
628 return;
629 }
Patrick Williams7b8786f2022-10-10 10:23:37 -0500630
Patrick Williamsb7077432024-08-16 15:22:21 -0400631 propertiesChangedCallback(systemConfiguration, objServer);
632 });
Brad Bishop10a8c5f2020-12-07 21:40:07 -0500633 // We also need a poke from DBus when new interfaces are created or
634 // destroyed.
Patrick Williams2af39222022-07-22 19:26:56 -0500635 sdbusplus::bus::match_t interfacesAddedMatch(
636 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -0500637 sdbusplus::bus::match::rules::interfacesAdded(),
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600638 [&](sdbusplus::message_t& msg) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400639 if (iaContainsProbeInterface(msg, probeInterfaces))
640 {
641 propertiesChangedCallback(systemConfiguration, objServer);
642 }
643 });
Patrick Williams2af39222022-07-22 19:26:56 -0500644 sdbusplus::bus::match_t interfacesRemovedMatch(
645 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -0500646 sdbusplus::bus::match::rules::interfacesRemoved(),
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600647 [&](sdbusplus::message_t& msg) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400648 if (irContainsProbeInterface(msg, probeInterfaces))
649 {
650 propertiesChangedCallback(systemConfiguration, objServer);
651 }
652 });
Brad Bishopc76af0f2020-12-04 13:50:23 -0500653
Ed Tanous49a888c2023-03-06 13:44:51 -0800654 boost::asio::post(io, [&]() {
655 propertiesChangedCallback(systemConfiguration, objServer);
656 });
James Feist4131aea2018-03-09 09:47:30 -0800657
James Feistfd1264a2018-05-03 12:10:00 -0700658 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -0700659 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800660 });
Christopher Meis12bea9b2025-04-03 10:14:42 +0200661 dbus_interface::tryIfaceInitialize(entityIface);
James Feist8f2710a2018-05-09 17:18:55 -0700662
James Feist1df06a42019-04-11 14:23:04 -0700663 if (fwVersionIsSame())
664 {
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200665 if (std::filesystem::is_regular_file(
666 configuration::currentConfiguration))
James Feist1df06a42019-04-11 14:23:04 -0700667 {
668 // this file could just be deleted, but it's nice for debug
669 std::filesystem::create_directory(tempConfigDir);
670 std::filesystem::remove(lastConfiguration);
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200671 std::filesystem::copy(configuration::currentConfiguration,
672 lastConfiguration);
673 std::filesystem::remove(configuration::currentConfiguration);
James Feist1df06a42019-04-11 14:23:04 -0700674
675 std::ifstream jsonStream(lastConfiguration);
676 if (jsonStream.good())
677 {
678 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
679 if (data.is_discarded())
680 {
Patrick Williamsb7077432024-08-16 15:22:21 -0400681 std::cerr
682 << "syntax error in " << lastConfiguration << "\n";
James Feist1df06a42019-04-11 14:23:04 -0700683 }
684 else
685 {
686 lastJson = std::move(data);
687 }
688 }
689 else
690 {
691 std::cerr << "unable to open " << lastConfiguration << "\n";
692 }
693 }
694 }
695 else
696 {
697 // not an error, just logging at this level to make it in the journal
698 std::cerr << "Clearing previous configuration\n";
Christopher Meisbdaa6b22025-04-02 10:49:02 +0200699 std::filesystem::remove(configuration::currentConfiguration);
James Feist1df06a42019-04-11 14:23:04 -0700700 }
701
702 // some boards only show up after power is on, we want to not say they are
703 // removed until the same state happens
Ed Tanous07d467b2021-02-23 14:48:37 -0800704 setupPowerMatch(systemBus);
James Feist1df06a42019-04-11 14:23:04 -0700705
James Feist1b2e2242018-01-30 13:45:19 -0800706 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -0800707
708 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -0800709}