blob: d3edf620ef5d199b329bc9e96e8c317ab9b44dcc [file] [log] [blame]
Alexander Hansen4e1142d2025-07-25 17:07:27 +02001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
James Feist3cb5fec2018-01-23 14:41:51 -08003
Brad Bishope45d8c72022-05-25 15:12:53 -04004#include "entity_manager.hpp"
James Feist1df06a42019-04-11 14:23:04 -07005
Christopher Meisfc9e7fd2025-04-03 13:13:35 +02006#include "../utils.hpp"
7#include "../variant_visitors.hpp"
Christopher Meisbdaa6b22025-04-02 10:49:02 +02008#include "configuration.hpp"
Christopher Meis12bea9b2025-04-03 10:14:42 +02009#include "dbus_interface.hpp"
Alexander Hansenf57a2592025-06-27 15:07:07 +020010#include "log_device_inventory.hpp"
Brad Bishope45d8c72022-05-25 15:12:53 -040011#include "overlay.hpp"
Christopher Meis26fbbd52025-03-26 14:55:06 +010012#include "perform_scan.hpp"
Alexander Hansen60803182025-06-27 14:41:28 +020013#include "phosphor-logging/lg2.hpp"
Benjamin Fairca2eb042022-09-13 06:40:42 +000014#include "topology.hpp"
Christopher Meis59ef1e72025-04-16 08:53:25 +020015#include "utils.hpp"
James Feist481c5d52019-08-13 14:40:40 -070016
James Feist11be6672018-04-06 14:05:32 -070017#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070018#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080019#include <boost/algorithm/string/predicate.hpp>
20#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070021#include <boost/algorithm/string/split.hpp>
James Feist02d2b932020-02-06 16:28:48 -080022#include <boost/asio/io_context.hpp>
Ed Tanous49a888c2023-03-06 13:44:51 -080023#include <boost/asio/post.hpp>
James Feistb1728ca2020-04-30 15:40:55 -070024#include <boost/asio/steady_timer.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080025#include <boost/container/flat_map.hpp>
26#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070027#include <boost/range/iterator_range.hpp>
James Feist8c505da2020-05-28 10:06:33 -070028#include <nlohmann/json.hpp>
29#include <sdbusplus/asio/connection.hpp>
30#include <sdbusplus/asio/object_server.hpp>
31
James Feist637b3ef2019-04-15 16:35:30 -070032#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080033#include <fstream>
Andrew Jefferye35d0ac2022-03-24 15:53:13 +103034#include <functional>
James Feista465ccc2019-02-08 12:51:01 -080035#include <iostream>
Andrew Jeffery666583b2021-12-01 15:50:12 +103036#include <map>
James Feista465ccc2019-02-08 12:51:01 -080037#include <regex>
James Feist1df06a42019-04-11 14:23:04 -070038constexpr const char* tempConfigDir = "/tmp/configuration/";
39constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
James Feistf1b14142019-04-10 15:22:09 -070040
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020041static constexpr std::array<const char*, 6> settableInterfaces = {
42 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist3cb5fec2018-01-23 14:41:51 -080043
Ed Tanous07d467b2021-02-23 14:48:37 -080044const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
45const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -080046
James Feista465ccc2019-02-08 12:51:01 -080047sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -070048{
49 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
50 interface) != settableInterfaces.end()
51 ? sdbusplus::asio::PropertyPermission::readWrite
52 : sdbusplus::asio::PropertyPermission::readOnly;
53}
54
Christopher Meiscf6a75b2025-06-03 07:53:50 +020055EntityManager::EntityManager(
Alexander Hansena555acf2025-06-27 11:59:10 +020056 std::shared_ptr<sdbusplus::asio::connection>& systemBus,
57 boost::asio::io_context& io) :
Christopher Meiscf6a75b2025-06-03 07:53:50 +020058 systemBus(systemBus),
59 objServer(sdbusplus::asio::object_server(systemBus, /*skipManager=*/true)),
60 lastJson(nlohmann::json::object()),
Alexander Hansenb1340da2025-06-27 14:29:13 +020061 systemConfiguration(nlohmann::json::object()), io(io),
Alexander Hansene1646272025-07-25 11:47:17 +020062 powerStatus(*systemBus), propertiesChangedTimer(io)
Christopher Meiscf6a75b2025-06-03 07:53:50 +020063{
64 // All other objects that EntityManager currently support are under the
65 // inventory subtree.
66 // See the discussion at
67 // https://discord.com/channels/775381525260664832/1018929092009144380
68 objServer.add_manager("/xyz/openbmc_project/inventory");
James Feist75fdeeb2018-02-20 14:26:16 -080069
Christopher Meiscf6a75b2025-06-03 07:53:50 +020070 entityIface = objServer.add_interface("/xyz/openbmc_project/EntityManager",
71 "xyz.openbmc_project.EntityManager");
72 entityIface->register_method("ReScan", [this]() {
73 propertiesChangedCallback();
74 });
75 dbus_interface::tryIfaceInitialize(entityIface);
Christopher Meisf7252572025-06-11 13:22:05 +020076
77 initFilters(configuration.probeInterfaces);
Christopher Meiscf6a75b2025-06-03 07:53:50 +020078}
79
80void EntityManager::postToDbus(const nlohmann::json& newConfiguration)
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 {
Alexander Hansen4fcd9462025-07-30 17:44:44 +020087 postBoardToDBus(boardId, boardConfig, newBoards);
James Feist1b2e2242018-01-30 13:45:19 -080088 }
Benjamin Fairca2eb042022-09-13 06:40:42 +000089
Matt Spinler6eb60972023-08-14 16:36:20 -050090 for (const auto& [assocPath, assocPropValue] :
91 topology.getAssocs(newBoards))
Benjamin Fairca2eb042022-09-13 06:40:42 +000092 {
Matt Spinler6eb60972023-08-14 16:36:20 -050093 auto findBoard = newBoards.find(assocPath);
94 if (findBoard == newBoards.end())
95 {
96 continue;
97 }
Benjamin Fairca2eb042022-09-13 06:40:42 +000098
Alexander Hansen57604ed2025-06-27 13:22:28 +020099 auto ifacePtr = dbus_interface.createInterface(
Matt Spinler6eb60972023-08-14 16:36:20 -0500100 objServer, assocPath, "xyz.openbmc_project.Association.Definitions",
101 findBoard->second);
102
103 ifacePtr->register_property("Associations", assocPropValue);
Christopher Meis12bea9b2025-04-03 10:14:42 +0200104 dbus_interface::tryIfaceInitialize(ifacePtr);
Benjamin Fairca2eb042022-09-13 06:40:42 +0000105 }
James Feist1b2e2242018-01-30 13:45:19 -0800106}
107
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200108void EntityManager::postBoardToDBus(
109 const std::string& boardId, const nlohmann::json& boardConfig,
110 std::map<std::string, std::string>& newBoards)
111{
112 std::string boardName = boardConfig["Name"];
113 std::string boardNameOrig = boardConfig["Name"];
114 std::string jsonPointerPath = "/" + boardId;
115 // loop through newConfiguration, but use values from system
116 // configuration to be able to modify via dbus later
117 auto boardValues = systemConfiguration[boardId];
118 auto findBoardType = boardValues.find("Type");
119 std::string boardType;
120 if (findBoardType != boardValues.end() &&
121 findBoardType->type() == nlohmann::json::value_t::string)
122 {
123 boardType = findBoardType->get<std::string>();
124 std::regex_replace(boardType.begin(), boardType.begin(),
125 boardType.end(), illegalDbusMemberRegex, "_");
126 }
127 else
128 {
129 std::cerr << "Unable to find type for " << boardName
130 << " reverting to Chassis.\n";
131 boardType = "Chassis";
132 }
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200133
Christopher Meis811160e2025-08-08 08:48:37 +0200134 const std::string boardPath =
135 em_utils::buildInventorySystemPath(boardName, boardType);
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200136
137 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
138 dbus_interface.createInterface(objServer, boardPath,
139 "xyz.openbmc_project.Inventory.Item",
140 boardName);
141
142 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
143 dbus_interface.createInterface(
144 objServer, boardPath,
145 "xyz.openbmc_project.Inventory.Item." + boardType, boardNameOrig);
146
147 dbus_interface.createAddObjectMethod(
148 io, jsonPointerPath, boardPath, systemConfiguration, objServer,
149 boardNameOrig);
150
151 dbus_interface::populateInterfaceFromJson(
152 io, systemConfiguration, jsonPointerPath, boardIface, boardValues,
153 objServer);
154 jsonPointerPath += "/";
155 // iterate through board properties
156 for (const auto& [propName, propValue] : boardValues.items())
157 {
158 if (propValue.type() == nlohmann::json::value_t::object)
159 {
160 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
161 dbus_interface.createInterface(objServer, boardPath, propName,
162 boardNameOrig);
163
164 dbus_interface::populateInterfaceFromJson(
165 io, systemConfiguration, jsonPointerPath + propName, iface,
166 propValue, objServer);
167 }
168 }
169
Alexander Hansen4fa40122025-07-30 18:26:35 +0200170 nlohmann::json::iterator exposes = boardValues.find("Exposes");
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200171 if (exposes == boardValues.end())
172 {
173 return;
174 }
175 // iterate through exposes
176 jsonPointerPath += "Exposes/";
177
178 // store the board level pointer so we can modify it on the way down
179 std::string jsonPointerPathBoard = jsonPointerPath;
180 size_t exposesIndex = -1;
Alexander Hansen4fa40122025-07-30 18:26:35 +0200181 for (nlohmann::json& item : *exposes)
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200182 {
Alexander Hansen4fa40122025-07-30 18:26:35 +0200183 postExposesRecordsToDBus(item, exposesIndex, boardNameOrig,
184 jsonPointerPath, jsonPointerPathBoard,
185 boardPath, boardType);
186 }
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200187
Alexander Hansen4fa40122025-07-30 18:26:35 +0200188 newBoards.emplace(boardPath, boardNameOrig);
189}
190
191void EntityManager::postExposesRecordsToDBus(
192 nlohmann::json& item, size_t& exposesIndex,
193 const std::string& boardNameOrig, std::string jsonPointerPath,
194 const std::string& jsonPointerPathBoard, const std::string& boardPath,
195 const std::string& boardType)
196{
197 exposesIndex++;
198 jsonPointerPath = jsonPointerPathBoard;
199 jsonPointerPath += std::to_string(exposesIndex);
200
201 auto findName = item.find("Name");
202 if (findName == item.end())
203 {
204 std::cerr << "cannot find name in field " << item << "\n";
205 return;
206 }
207 auto findStatus = item.find("Status");
208 // if status is not found it is assumed to be status = 'okay'
209 if (findStatus != item.end())
210 {
211 if (*findStatus == "disabled")
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200212 {
Alexander Hansen4fa40122025-07-30 18:26:35 +0200213 return;
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200214 }
Alexander Hansen4fa40122025-07-30 18:26:35 +0200215 }
216 auto findType = item.find("Type");
217 std::string itemType;
218 if (findType != item.end())
219 {
220 itemType = findType->get<std::string>();
221 std::regex_replace(itemType.begin(), itemType.begin(), itemType.end(),
222 illegalDbusPathRegex, "_");
223 }
224 else
225 {
226 itemType = "unknown";
227 }
228 std::string itemName = findName->get<std::string>();
229 std::regex_replace(itemName.begin(), itemName.begin(), itemName.end(),
230 illegalDbusMemberRegex, "_");
231 std::string ifacePath = boardPath;
232 ifacePath += "/";
233 ifacePath += itemName;
234
235 if (itemType == "BMC")
236 {
237 std::shared_ptr<sdbusplus::asio::dbus_interface> bmcIface =
238 dbus_interface.createInterface(
239 objServer, ifacePath, "xyz.openbmc_project.Inventory.Item.Bmc",
240 boardNameOrig);
241 dbus_interface::populateInterfaceFromJson(
242 io, systemConfiguration, jsonPointerPath, bmcIface, item, objServer,
243 getPermission(itemType));
244 }
245 else if (itemType == "System")
246 {
247 std::shared_ptr<sdbusplus::asio::dbus_interface> systemIface =
248 dbus_interface.createInterface(
249 objServer, ifacePath,
250 "xyz.openbmc_project.Inventory.Item.System", boardNameOrig);
251 dbus_interface::populateInterfaceFromJson(
252 io, systemConfiguration, jsonPointerPath, systemIface, item,
253 objServer, getPermission(itemType));
254 }
255
256 for (const auto& [name, config] : item.items())
257 {
258 jsonPointerPath = jsonPointerPathBoard;
259 jsonPointerPath.append(std::to_string(exposesIndex))
260 .append("/")
261 .append(name);
262 if (config.type() == nlohmann::json::value_t::object)
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200263 {
Alexander Hansen4fa40122025-07-30 18:26:35 +0200264 std::string ifaceName = "xyz.openbmc_project.Configuration.";
265 ifaceName.append(itemType).append(".").append(name);
266
267 std::shared_ptr<sdbusplus::asio::dbus_interface> objectIface =
268 dbus_interface.createInterface(objServer, ifacePath, ifaceName,
269 boardNameOrig);
270
271 dbus_interface::populateInterfaceFromJson(
272 io, systemConfiguration, jsonPointerPath, objectIface, config,
273 objServer, getPermission(name));
274 }
275 else if (config.type() == nlohmann::json::value_t::array)
276 {
277 size_t index = 0;
278 if (config.empty())
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200279 {
280 continue;
281 }
Alexander Hansen4fa40122025-07-30 18:26:35 +0200282 bool isLegal = true;
283 auto type = config[0].type();
284 if (type != nlohmann::json::value_t::object)
285 {
286 continue;
287 }
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200288
Alexander Hansen4fa40122025-07-30 18:26:35 +0200289 // verify legal json
290 for (const auto& arrayItem : config)
291 {
292 if (arrayItem.type() != type)
293 {
294 isLegal = false;
295 break;
296 }
297 }
298 if (!isLegal)
299 {
300 std::cerr << "dbus format error" << config << "\n";
301 break;
302 }
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200303
Alexander Hansen4fa40122025-07-30 18:26:35 +0200304 for (auto& arrayItem : config)
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200305 {
306 std::string ifaceName = "xyz.openbmc_project.Configuration.";
307 ifaceName.append(itemType).append(".").append(name);
Alexander Hansen4fa40122025-07-30 18:26:35 +0200308 ifaceName.append(std::to_string(index));
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200309
310 std::shared_ptr<sdbusplus::asio::dbus_interface> objectIface =
311 dbus_interface.createInterface(objServer, ifacePath,
312 ifaceName, boardNameOrig);
313
314 dbus_interface::populateInterfaceFromJson(
Alexander Hansen4fa40122025-07-30 18:26:35 +0200315 io, systemConfiguration,
316 jsonPointerPath + "/" + std::to_string(index), objectIface,
317 arrayItem, objServer, getPermission(name));
318 index++;
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200319 }
320 }
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200321 }
322
Alexander Hansen4fa40122025-07-30 18:26:35 +0200323 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
324 dbus_interface.createInterface(
325 objServer, ifacePath,
326 "xyz.openbmc_project.Configuration." + itemType, boardNameOrig);
327
328 dbus_interface::populateInterfaceFromJson(
329 io, systemConfiguration, jsonPointerPath, itemIface, item, objServer,
330 getPermission(itemType));
331
332 topology.addBoard(boardPath, boardType, boardNameOrig, item);
Alexander Hansen4fcd9462025-07-30 17:44:44 +0200333}
334
Andrew Jeffery55192932022-03-24 12:29:27 +1030335static bool deviceRequiresPowerOn(const nlohmann::json& entity)
336{
337 auto powerState = entity.find("PowerState");
Andrew Jefferyb6209442022-03-24 12:36:20 +1030338 if (powerState == entity.end())
Andrew Jeffery55192932022-03-24 12:29:27 +1030339 {
Andrew Jefferyb6209442022-03-24 12:36:20 +1030340 return false;
Andrew Jeffery55192932022-03-24 12:29:27 +1030341 }
342
Ed Tanous3013fb42022-07-09 08:27:06 -0700343 const auto* ptr = powerState->get_ptr<const std::string*>();
344 if (ptr == nullptr)
Andrew Jefferyb6209442022-03-24 12:36:20 +1030345 {
346 return false;
347 }
348
349 return *ptr == "On" || *ptr == "BiosPost";
Andrew Jeffery55192932022-03-24 12:29:27 +1030350}
351
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030352static void pruneDevice(const nlohmann::json& systemConfiguration,
353 const bool powerOff, const bool scannedPowerOff,
354 const std::string& name, const nlohmann::json& device)
355{
356 if (systemConfiguration.contains(name))
357 {
358 return;
359 }
360
Andrew Jeffery4db38bc2022-03-24 13:42:41 +1030361 if (deviceRequiresPowerOn(device) && (powerOff || scannedPowerOff))
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030362 {
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030363 return;
364 }
365
366 logDeviceRemoved(device);
367}
368
Alexander Hansen95ab18f2025-06-27 13:58:10 +0200369void EntityManager::startRemovedTimer(boost::asio::steady_timer& timer,
370 nlohmann::json& systemConfiguration)
James Feist1df06a42019-04-11 14:23:04 -0700371{
James Feistfb00f392019-06-25 14:16:48 -0700372 if (systemConfiguration.empty() || lastJson.empty())
373 {
374 return; // not ready yet
375 }
James Feist1df06a42019-04-11 14:23:04 -0700376 if (scannedPowerOn)
377 {
378 return;
379 }
380
Alexander Hansen0ab32b32025-06-27 14:50:33 +0200381 if (!powerStatus.isPowerOn() && scannedPowerOff)
James Feist1df06a42019-04-11 14:23:04 -0700382 {
383 return;
384 }
385
James Feistb1728ca2020-04-30 15:40:55 -0700386 timer.expires_after(std::chrono::seconds(10));
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030387 timer.async_wait(
Alexander Hansen95ab18f2025-06-27 13:58:10 +0200388 [&systemConfiguration, this](const boost::system::error_code& ec) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400389 if (ec == boost::asio::error::operation_aborted)
390 {
391 return;
392 }
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030393
Alexander Hansen0ab32b32025-06-27 14:50:33 +0200394 bool powerOff = !powerStatus.isPowerOn();
Patrick Williamsb7077432024-08-16 15:22:21 -0400395 for (const auto& [name, device] : lastJson.items())
396 {
397 pruneDevice(systemConfiguration, powerOff, scannedPowerOff,
398 name, device);
399 }
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030400
Patrick Williamsb7077432024-08-16 15:22:21 -0400401 scannedPowerOff = true;
402 if (!powerOff)
403 {
404 scannedPowerOn = true;
405 }
406 });
James Feist1df06a42019-04-11 14:23:04 -0700407}
408
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200409void EntityManager::pruneConfiguration(bool powerOff, const std::string& name,
410 const nlohmann::json& device)
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030411{
412 if (powerOff && deviceRequiresPowerOn(device))
413 {
414 // power not on yet, don't know if it's there or not
415 return;
416 }
417
Alexander Hansen57604ed2025-06-27 13:22:28 +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
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200434void EntityManager::publishNewConfiguration(
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030435 const size_t& instance, const size_t count,
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200436 boost::asio::steady_timer& timer, // Gerrit discussion:
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030437 // 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)
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200443 const nlohmann::json newConfiguration)
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030444{
Alexander Hansena555acf2025-06-27 11:59:10 +0200445 loadOverlays(newConfiguration, io);
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030446
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200447 boost::asio::post(io, [this]() {
Christopher Meisf7252572025-06-11 13:22:05 +0200448 if (!writeJsonFiles(systemConfiguration))
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030449 {
450 std::cerr << "Error writing json files\n";
451 }
452 });
453
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200454 boost::asio::post(io, [this, &instance, count, &timer, newConfiguration]() {
455 postToDbus(newConfiguration);
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030456 if (count == instance)
457 {
Alexander Hansen95ab18f2025-06-27 13:58:10 +0200458 startRemovedTimer(timer, systemConfiguration);
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030459 }
460 });
461}
462
James Feist8f2710a2018-05-09 17:18:55 -0700463// main properties changed entry
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200464void EntityManager::propertiesChangedCallback()
James Feist8f2710a2018-05-09 17:18:55 -0700465{
Alexander Hansenfc1b1e22025-06-27 14:34:11 +0200466 propertiesChangedInstance++;
467 size_t count = propertiesChangedInstance;
James Feist1df06a42019-04-11 14:23:04 -0700468
Alexander Hansenb1340da2025-06-27 14:29:13 +0200469 propertiesChangedTimer.expires_after(std::chrono::milliseconds(500));
James Feist8f2710a2018-05-09 17:18:55 -0700470
471 // setup an async wait as we normally get flooded with new requests
Alexander Hansenb1340da2025-06-27 14:29:13 +0200472 propertiesChangedTimer.async_wait(
473 [this, count](const boost::system::error_code& ec) {
474 if (ec == boost::asio::error::operation_aborted)
475 {
476 // we were cancelled
477 return;
478 }
479 if (ec)
480 {
481 std::cerr << "async wait error " << ec << "\n";
482 return;
483 }
James Feist8f2710a2018-05-09 17:18:55 -0700484
Alexander Hansenb1340da2025-06-27 14:29:13 +0200485 if (propertiesChangedInProgress)
486 {
487 propertiesChangedCallback();
488 return;
489 }
490 propertiesChangedInProgress = true;
James Feist2539ccd2020-05-01 16:15:08 -0700491
Alexander Hansenb1340da2025-06-27 14:29:13 +0200492 nlohmann::json oldConfiguration = systemConfiguration;
493 auto missingConfigurations = std::make_shared<nlohmann::json>();
494 *missingConfigurations = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -0700495
Alexander Hansenb1340da2025-06-27 14:29:13 +0200496 auto perfScan = std::make_shared<scan::PerformScan>(
Christopher Meisf7252572025-06-11 13:22:05 +0200497 *this, *missingConfigurations, configuration.configurations, io,
Alexander Hansenb1340da2025-06-27 14:29:13 +0200498 [this, count, oldConfiguration, missingConfigurations]() {
499 // this is something that since ac has been applied to the
500 // bmc we saw, and we no longer see it
Alexander Hansen0ab32b32025-06-27 14:50:33 +0200501 bool powerOff = !powerStatus.isPowerOn();
Alexander Hansenb1340da2025-06-27 14:29:13 +0200502 for (const auto& [name, device] :
503 missingConfigurations->items())
504 {
505 pruneConfiguration(powerOff, name, device);
506 }
Alexander Hansenb1340da2025-06-27 14:29:13 +0200507 nlohmann::json newConfiguration = systemConfiguration;
508
Christopher Meisf7252572025-06-11 13:22:05 +0200509 deriveNewConfiguration(oldConfiguration, newConfiguration);
Alexander Hansenb1340da2025-06-27 14:29:13 +0200510
511 for (const auto& [_, device] : newConfiguration.items())
512 {
513 logDeviceAdded(device);
514 }
515
516 propertiesChangedInProgress = false;
517
518 boost::asio::post(io, [this, newConfiguration, count] {
519 publishNewConfiguration(
Alexander Hansenfc1b1e22025-06-27 14:34:11 +0200520 std::ref(propertiesChangedInstance), count,
Alexander Hansenb1340da2025-06-27 14:29:13 +0200521 std::ref(propertiesChangedTimer), newConfiguration);
522 });
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200523 });
Alexander Hansenb1340da2025-06-27 14:29:13 +0200524 perfScan->run();
525 });
James Feist75fdeeb2018-02-20 14:26:16 -0800526}
527
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600528// Check if InterfacesAdded payload contains an iface that needs probing.
Patrick Williamsb7077432024-08-16 15:22:21 -0400529static bool iaContainsProbeInterface(
Christopher Meisf7252572025-06-11 13:22:05 +0200530 sdbusplus::message_t& msg,
531 const std::unordered_set<std::string>& probeInterfaces)
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600532{
533 sdbusplus::message::object_path path;
534 DBusObject interfaces;
535 std::set<std::string> interfaceSet;
536 std::set<std::string> intersect;
537
538 msg.read(path, interfaces);
539
540 std::for_each(interfaces.begin(), interfaces.end(),
541 [&interfaceSet](const auto& iface) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400542 interfaceSet.insert(iface.first);
543 });
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600544
545 std::set_intersection(interfaceSet.begin(), interfaceSet.end(),
546 probeInterfaces.begin(), probeInterfaces.end(),
547 std::inserter(intersect, intersect.end()));
548 return !intersect.empty();
549}
550
551// Check if InterfacesRemoved payload contains an iface that needs probing.
Patrick Williamsb7077432024-08-16 15:22:21 -0400552static bool irContainsProbeInterface(
Christopher Meisf7252572025-06-11 13:22:05 +0200553 sdbusplus::message_t& msg,
554 const std::unordered_set<std::string>& probeInterfaces)
Matt Spinler8d1ac3a2023-02-02 09:48:04 -0600555{
556 sdbusplus::message::object_path path;
557 std::set<std::string> interfaces;
558 std::set<std::string> intersect;
559
560 msg.read(path, interfaces);
561
562 std::set_intersection(interfaces.begin(), interfaces.end(),
563 probeInterfaces.begin(), probeInterfaces.end(),
564 std::inserter(intersect, intersect.end()));
565 return !intersect.empty();
566}
567
Alexander Hansenad28e402025-06-25 12:38:20 +0200568void EntityManager::handleCurrentConfigurationJson()
569{
570 if (em_utils::fwVersionIsSame())
571 {
Christopher Meisf7252572025-06-11 13:22:05 +0200572 if (std::filesystem::is_regular_file(currentConfiguration))
Alexander Hansenad28e402025-06-25 12:38:20 +0200573 {
574 // this file could just be deleted, but it's nice for debug
575 std::filesystem::create_directory(tempConfigDir);
576 std::filesystem::remove(lastConfiguration);
Christopher Meisf7252572025-06-11 13:22:05 +0200577 std::filesystem::copy(currentConfiguration, lastConfiguration);
578 std::filesystem::remove(currentConfiguration);
Alexander Hansenad28e402025-06-25 12:38:20 +0200579
580 std::ifstream jsonStream(lastConfiguration);
581 if (jsonStream.good())
582 {
583 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
584 if (data.is_discarded())
585 {
586 std::cerr
587 << "syntax error in " << lastConfiguration << "\n";
588 }
589 else
590 {
591 lastJson = std::move(data);
592 }
593 }
594 else
595 {
596 std::cerr << "unable to open " << lastConfiguration << "\n";
597 }
598 }
599 }
600 else
601 {
602 // not an error, just logging at this level to make it in the journal
603 std::cerr << "Clearing previous configuration\n";
Christopher Meisf7252572025-06-11 13:22:05 +0200604 std::filesystem::remove(currentConfiguration);
Alexander Hansenad28e402025-06-25 12:38:20 +0200605 }
606}
607
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200608void EntityManager::registerCallback(const std::string& path)
James Feist75fdeeb2018-02-20 14:26:16 -0800609{
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200610 if (dbusMatches.contains(path))
611 {
612 return;
613 }
Nan Zhoua3315672022-09-20 19:48:14 +0000614
Alexander Hansen60803182025-06-27 14:41:28 +0200615 lg2::debug("creating PropertiesChanged match on {PATH}", "PATH", path);
616
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200617 std::function<void(sdbusplus::message_t & message)> eventHandler =
618 [&](sdbusplus::message_t&) { propertiesChangedCallback(); };
James Feistfd1264a2018-05-03 12:10:00 -0700619
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200620 sdbusplus::bus::match_t match(
621 static_cast<sdbusplus::bus_t&>(*systemBus),
622 "type='signal',member='PropertiesChanged',path='" + path + "'",
623 eventHandler);
624 dbusMatches.emplace(path, std::move(match));
625}
James Feistfd1264a2018-05-03 12:10:00 -0700626
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200627// We need a poke from DBus for static providers that create all their
628// objects prior to claiming a well-known name, and thus don't emit any
629// org.freedesktop.DBus.Properties signals. Similarly if a process exits
630// for any reason, expected or otherwise, we'll need a poke to remove
631// entities from DBus.
Christopher Meisf7252572025-06-11 13:22:05 +0200632void EntityManager::initFilters(
633 const std::unordered_set<std::string>& probeInterfaces)
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200634{
Alexander Hansen0f7bd782025-06-27 13:39:53 +0200635 nameOwnerChangedMatch = std::make_unique<sdbusplus::bus::match_t>(
Patrick Williams2af39222022-07-22 19:26:56 -0500636 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishopc76af0f2020-12-04 13:50:23 -0500637 sdbusplus::bus::match::rules::nameOwnerChanged(),
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200638 [this](sdbusplus::message_t& m) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400639 auto [name, oldOwner,
640 newOwner] = m.unpack<std::string, std::string, std::string>();
Patrick Williams7b8786f2022-10-10 10:23:37 -0500641
Patrick Williamsb7077432024-08-16 15:22:21 -0400642 if (name.starts_with(':'))
643 {
644 // We should do nothing with unique-name connections.
645 return;
646 }
Patrick Williams7b8786f2022-10-10 10:23:37 -0500647
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200648 propertiesChangedCallback();
Patrick Williamsb7077432024-08-16 15:22:21 -0400649 });
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200650
Brad Bishop10a8c5f2020-12-07 21:40:07 -0500651 // We also need a poke from DBus when new interfaces are created or
652 // destroyed.
Alexander Hansen0f7bd782025-06-27 13:39:53 +0200653 interfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
Patrick Williams2af39222022-07-22 19:26:56 -0500654 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -0500655 sdbusplus::bus::match::rules::interfacesAdded(),
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200656 [this, probeInterfaces](sdbusplus::message_t& msg) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400657 if (iaContainsProbeInterface(msg, probeInterfaces))
658 {
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200659 propertiesChangedCallback();
Patrick Williamsb7077432024-08-16 15:22:21 -0400660 }
661 });
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200662
Alexander Hansen0f7bd782025-06-27 13:39:53 +0200663 interfacesRemovedMatch = std::make_unique<sdbusplus::bus::match_t>(
Patrick Williams2af39222022-07-22 19:26:56 -0500664 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -0500665 sdbusplus::bus::match::rules::interfacesRemoved(),
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200666 [this, probeInterfaces](sdbusplus::message_t& msg) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400667 if (irContainsProbeInterface(msg, probeInterfaces))
668 {
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200669 propertiesChangedCallback();
Patrick Williamsb7077432024-08-16 15:22:21 -0400670 }
671 });
Christopher Meiscf6a75b2025-06-03 07:53:50 +0200672}