xref: /openbmc/entity-manager/src/entity_manager.cpp (revision 5c1a61a84069b48ccc27e45ffc82d5ca87b330a1)
1e45d8c71SBrad Bishop /*
2e45d8c71SBrad Bishop // Copyright (c) 2018 Intel Corporation
3e45d8c71SBrad Bishop //
4e45d8c71SBrad Bishop // Licensed under the Apache License, Version 2.0 (the "License");
5e45d8c71SBrad Bishop // you may not use this file except in compliance with the License.
6e45d8c71SBrad Bishop // You may obtain a copy of the License at
7e45d8c71SBrad Bishop //
8e45d8c71SBrad Bishop //      http://www.apache.org/licenses/LICENSE-2.0
9e45d8c71SBrad Bishop //
10e45d8c71SBrad Bishop // Unless required by applicable law or agreed to in writing, software
11e45d8c71SBrad Bishop // distributed under the License is distributed on an "AS IS" BASIS,
12e45d8c71SBrad Bishop // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e45d8c71SBrad Bishop // See the License for the specific language governing permissions and
14e45d8c71SBrad Bishop // limitations under the License.
15e45d8c71SBrad Bishop */
16e45d8c71SBrad Bishop /// \file entity_manager.cpp
17e45d8c71SBrad Bishop 
18e45d8c71SBrad Bishop #include "entity_manager.hpp"
19e45d8c71SBrad Bishop 
20e45d8c71SBrad Bishop #include "overlay.hpp"
21ca2eb04eSBenjamin Fair #include "topology.hpp"
22e45d8c71SBrad Bishop #include "utils.hpp"
23e45d8c71SBrad Bishop #include "variant_visitors.hpp"
24e45d8c71SBrad Bishop 
25e45d8c71SBrad Bishop #include <boost/algorithm/string/case_conv.hpp>
26e45d8c71SBrad Bishop #include <boost/algorithm/string/classification.hpp>
27e45d8c71SBrad Bishop #include <boost/algorithm/string/predicate.hpp>
28e45d8c71SBrad Bishop #include <boost/algorithm/string/replace.hpp>
29e45d8c71SBrad Bishop #include <boost/algorithm/string/split.hpp>
30e45d8c71SBrad Bishop #include <boost/asio/io_context.hpp>
3149a888c2SEd Tanous #include <boost/asio/post.hpp>
32e45d8c71SBrad Bishop #include <boost/asio/steady_timer.hpp>
33e45d8c71SBrad Bishop #include <boost/container/flat_map.hpp>
34e45d8c71SBrad Bishop #include <boost/container/flat_set.hpp>
35e45d8c71SBrad Bishop #include <boost/range/iterator_range.hpp>
36e45d8c71SBrad Bishop #include <nlohmann/json.hpp>
37e45d8c71SBrad Bishop #include <sdbusplus/asio/connection.hpp>
38e45d8c71SBrad Bishop #include <sdbusplus/asio/object_server.hpp>
39e45d8c71SBrad Bishop 
40e45d8c71SBrad Bishop #include <charconv>
41e45d8c71SBrad Bishop #include <filesystem>
42e45d8c71SBrad Bishop #include <fstream>
43e45d8c71SBrad Bishop #include <functional>
44e45d8c71SBrad Bishop #include <iostream>
45e45d8c71SBrad Bishop #include <map>
46e45d8c71SBrad Bishop #include <regex>
47e45d8c71SBrad Bishop #include <variant>
48e45d8c71SBrad Bishop constexpr const char* hostConfigurationDirectory = SYSCONF_DIR "configurations";
49e45d8c71SBrad Bishop constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
50e45d8c71SBrad Bishop constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
51e45d8c71SBrad Bishop constexpr const char* tempConfigDir = "/tmp/configuration/";
52e45d8c71SBrad Bishop constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
53e45d8c71SBrad Bishop constexpr const char* currentConfiguration = "/var/configuration/system.json";
54e45d8c71SBrad Bishop constexpr const char* globalSchema = "global.json";
55e45d8c71SBrad Bishop 
56e45d8c71SBrad Bishop const boost::container::flat_map<const char*, probe_type_codes, CmpStr>
57e45d8c71SBrad Bishop     probeTypes{{{"FALSE", probe_type_codes::FALSE_T},
58e45d8c71SBrad Bishop                 {"TRUE", probe_type_codes::TRUE_T},
59e45d8c71SBrad Bishop                 {"AND", probe_type_codes::AND},
60e45d8c71SBrad Bishop                 {"OR", probe_type_codes::OR},
61e45d8c71SBrad Bishop                 {"FOUND", probe_type_codes::FOUND},
62e45d8c71SBrad Bishop                 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
63e45d8c71SBrad Bishop 
64e45d8c71SBrad Bishop static constexpr std::array<const char*, 6> settableInterfaces = {
65e45d8c71SBrad Bishop     "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
66e45d8c71SBrad Bishop using JsonVariantType =
67e45d8c71SBrad Bishop     std::variant<std::vector<std::string>, std::vector<double>, std::string,
68e45d8c71SBrad Bishop                  int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
69e45d8c71SBrad Bishop                  uint16_t, uint8_t, bool>;
70e45d8c71SBrad Bishop 
71fc171428SEd Tanous // NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
72e45d8c71SBrad Bishop // store reference to all interfaces so we can destroy them later
73e45d8c71SBrad Bishop boost::container::flat_map<
74e45d8c71SBrad Bishop     std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
75e45d8c71SBrad Bishop     inventory;
76e45d8c71SBrad Bishop 
77e45d8c71SBrad Bishop // todo: pass this through nicer
78e45d8c71SBrad Bishop std::shared_ptr<sdbusplus::asio::connection> systemBus;
79e45d8c71SBrad Bishop nlohmann::json lastJson;
806eb60972SMatt Spinler Topology topology;
81e45d8c71SBrad Bishop 
82e45d8c71SBrad Bishop boost::asio::io_context io;
83fc171428SEd Tanous // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
84e45d8c71SBrad Bishop 
85e45d8c71SBrad Bishop const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
86e45d8c71SBrad Bishop const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
87e45d8c71SBrad Bishop 
tryIfaceInitialize(std::shared_ptr<sdbusplus::asio::dbus_interface> & iface)88d97c6319SJohn Edward Broadbent void tryIfaceInitialize(std::shared_ptr<sdbusplus::asio::dbus_interface>& iface)
89d97c6319SJohn Edward Broadbent {
90d97c6319SJohn Edward Broadbent     try
91d97c6319SJohn Edward Broadbent     {
92d97c6319SJohn Edward Broadbent         iface->initialize();
93d97c6319SJohn Edward Broadbent     }
94d97c6319SJohn Edward Broadbent     catch (std::exception& e)
95d97c6319SJohn Edward Broadbent     {
96d97c6319SJohn Edward Broadbent         std::cerr << "Unable to initialize dbus interface : " << e.what()
97d97c6319SJohn Edward Broadbent                   << "\n"
98d97c6319SJohn Edward Broadbent                   << "object Path : " << iface->get_object_path() << "\n"
99d97c6319SJohn Edward Broadbent                   << "interface name : " << iface->get_interface_name() << "\n";
100d97c6319SJohn Edward Broadbent     }
101d97c6319SJohn Edward Broadbent }
102d97c6319SJohn Edward Broadbent 
findProbeType(const std::string & probe)103e45d8c71SBrad Bishop FoundProbeTypeT findProbeType(const std::string& probe)
104e45d8c71SBrad Bishop {
105e45d8c71SBrad Bishop     boost::container::flat_map<const char*, probe_type_codes,
106e45d8c71SBrad Bishop                                CmpStr>::const_iterator probeType;
107e45d8c71SBrad Bishop     for (probeType = probeTypes.begin(); probeType != probeTypes.end();
108e45d8c71SBrad Bishop          ++probeType)
109e45d8c71SBrad Bishop     {
110e45d8c71SBrad Bishop         if (probe.find(probeType->first) != std::string::npos)
111e45d8c71SBrad Bishop         {
112e45d8c71SBrad Bishop             return probeType;
113e45d8c71SBrad Bishop         }
114e45d8c71SBrad Bishop     }
115e45d8c71SBrad Bishop 
116e45d8c71SBrad Bishop     return std::nullopt;
117e45d8c71SBrad Bishop }
118e45d8c71SBrad Bishop 
119e45d8c71SBrad Bishop static std::shared_ptr<sdbusplus::asio::dbus_interface>
createInterface(sdbusplus::asio::object_server & objServer,const std::string & path,const std::string & interface,const std::string & parent,bool checkNull=false)120e45d8c71SBrad Bishop     createInterface(sdbusplus::asio::object_server& objServer,
121e45d8c71SBrad Bishop                     const std::string& path, const std::string& interface,
122e45d8c71SBrad Bishop                     const std::string& parent, bool checkNull = false)
123e45d8c71SBrad Bishop {
124e45d8c71SBrad Bishop     // on first add we have no reason to check for null before add, as there
125e45d8c71SBrad Bishop     // won't be any. For dynamically added interfaces, we check for null so that
126e45d8c71SBrad Bishop     // a constant delete/add will not create a memory leak
127e45d8c71SBrad Bishop 
128e45d8c71SBrad Bishop     auto ptr = objServer.add_interface(path, interface);
129e45d8c71SBrad Bishop     auto& dataVector = inventory[parent];
130e45d8c71SBrad Bishop     if (checkNull)
131e45d8c71SBrad Bishop     {
132e45d8c71SBrad Bishop         auto it = std::find_if(dataVector.begin(), dataVector.end(),
133e45d8c71SBrad Bishop                                [](const auto& p) { return p.expired(); });
134e45d8c71SBrad Bishop         if (it != dataVector.end())
135e45d8c71SBrad Bishop         {
136e45d8c71SBrad Bishop             *it = ptr;
137e45d8c71SBrad Bishop             return ptr;
138e45d8c71SBrad Bishop         }
139e45d8c71SBrad Bishop     }
140e45d8c71SBrad Bishop     dataVector.emplace_back(ptr);
141e45d8c71SBrad Bishop     return ptr;
142e45d8c71SBrad Bishop }
143e45d8c71SBrad Bishop 
144e45d8c71SBrad Bishop // writes output files to persist data
writeJsonFiles(const nlohmann::json & systemConfiguration)145e45d8c71SBrad Bishop bool writeJsonFiles(const nlohmann::json& systemConfiguration)
146e45d8c71SBrad Bishop {
147e45d8c71SBrad Bishop     std::filesystem::create_directory(configurationOutDir);
148e45d8c71SBrad Bishop     std::ofstream output(currentConfiguration);
149e45d8c71SBrad Bishop     if (!output.good())
150e45d8c71SBrad Bishop     {
151e45d8c71SBrad Bishop         return false;
152e45d8c71SBrad Bishop     }
153e45d8c71SBrad Bishop     output << systemConfiguration.dump(4);
154e45d8c71SBrad Bishop     output.close();
155e45d8c71SBrad Bishop     return true;
156e45d8c71SBrad Bishop }
157e45d8c71SBrad Bishop 
158e45d8c71SBrad Bishop template <typename JsonType>
setJsonFromPointer(const std::string & ptrStr,const JsonType & value,nlohmann::json & systemConfiguration)159e45d8c71SBrad Bishop bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
160e45d8c71SBrad Bishop                         nlohmann::json& systemConfiguration)
161e45d8c71SBrad Bishop {
162e45d8c71SBrad Bishop     try
163e45d8c71SBrad Bishop     {
164e45d8c71SBrad Bishop         nlohmann::json::json_pointer ptr(ptrStr);
165e45d8c71SBrad Bishop         nlohmann::json& ref = systemConfiguration[ptr];
166e45d8c71SBrad Bishop         ref = value;
167e45d8c71SBrad Bishop         return true;
168e45d8c71SBrad Bishop     }
169e45d8c71SBrad Bishop     catch (const std::out_of_range&)
170e45d8c71SBrad Bishop     {
171e45d8c71SBrad Bishop         return false;
172e45d8c71SBrad Bishop     }
173e45d8c71SBrad Bishop }
174e45d8c71SBrad Bishop 
175e45d8c71SBrad Bishop // template function to add array as dbus property
176e45d8c71SBrad Bishop template <typename PropertyType>
addArrayToDbus(const std::string & name,const nlohmann::json & array,sdbusplus::asio::dbus_interface * iface,sdbusplus::asio::PropertyPermission permission,nlohmann::json & systemConfiguration,const std::string & jsonPointerString)177e45d8c71SBrad Bishop void addArrayToDbus(const std::string& name, const nlohmann::json& array,
178e45d8c71SBrad Bishop                     sdbusplus::asio::dbus_interface* iface,
179e45d8c71SBrad Bishop                     sdbusplus::asio::PropertyPermission permission,
180e45d8c71SBrad Bishop                     nlohmann::json& systemConfiguration,
181e45d8c71SBrad Bishop                     const std::string& jsonPointerString)
182e45d8c71SBrad Bishop {
183e45d8c71SBrad Bishop     std::vector<PropertyType> values;
184e45d8c71SBrad Bishop     for (const auto& property : array)
185e45d8c71SBrad Bishop     {
186e45d8c71SBrad Bishop         auto ptr = property.get_ptr<const PropertyType*>();
187e45d8c71SBrad Bishop         if (ptr != nullptr)
188e45d8c71SBrad Bishop         {
189e45d8c71SBrad Bishop             values.emplace_back(*ptr);
190e45d8c71SBrad Bishop         }
191e45d8c71SBrad Bishop     }
192e45d8c71SBrad Bishop 
193e45d8c71SBrad Bishop     if (permission == sdbusplus::asio::PropertyPermission::readOnly)
194e45d8c71SBrad Bishop     {
195e45d8c71SBrad Bishop         iface->register_property(name, values);
196e45d8c71SBrad Bishop     }
197e45d8c71SBrad Bishop     else
198e45d8c71SBrad Bishop     {
199e45d8c71SBrad Bishop         iface->register_property(
200e45d8c71SBrad Bishop             name, values,
201e45d8c71SBrad Bishop             [&systemConfiguration,
202e45d8c71SBrad Bishop              jsonPointerString{std::string(jsonPointerString)}](
203e45d8c71SBrad Bishop                 const std::vector<PropertyType>& newVal,
204e45d8c71SBrad Bishop                 std::vector<PropertyType>& val) {
205e45d8c71SBrad Bishop                 val = newVal;
206e45d8c71SBrad Bishop                 if (!setJsonFromPointer(jsonPointerString, val,
207e45d8c71SBrad Bishop                                         systemConfiguration))
208e45d8c71SBrad Bishop                 {
209e45d8c71SBrad Bishop                     std::cerr << "error setting json field\n";
210e45d8c71SBrad Bishop                     return -1;
211e45d8c71SBrad Bishop                 }
212e45d8c71SBrad Bishop                 if (!writeJsonFiles(systemConfiguration))
213e45d8c71SBrad Bishop                 {
214e45d8c71SBrad Bishop                     std::cerr << "error setting json file\n";
215e45d8c71SBrad Bishop                     return -1;
216e45d8c71SBrad Bishop                 }
217e45d8c71SBrad Bishop                 return 1;
218e45d8c71SBrad Bishop             });
219e45d8c71SBrad Bishop     }
220e45d8c71SBrad Bishop }
221e45d8c71SBrad Bishop 
222e45d8c71SBrad Bishop template <typename PropertyType>
addProperty(const std::string & name,const PropertyType & value,sdbusplus::asio::dbus_interface * iface,nlohmann::json & systemConfiguration,const std::string & jsonPointerString,sdbusplus::asio::PropertyPermission permission)2233013fb49SEd Tanous void addProperty(const std::string& name, const PropertyType& value,
224e45d8c71SBrad Bishop                  sdbusplus::asio::dbus_interface* iface,
225e45d8c71SBrad Bishop                  nlohmann::json& systemConfiguration,
226e45d8c71SBrad Bishop                  const std::string& jsonPointerString,
227e45d8c71SBrad Bishop                  sdbusplus::asio::PropertyPermission permission)
228e45d8c71SBrad Bishop {
229e45d8c71SBrad Bishop     if (permission == sdbusplus::asio::PropertyPermission::readOnly)
230e45d8c71SBrad Bishop     {
2313013fb49SEd Tanous         iface->register_property(name, value);
232e45d8c71SBrad Bishop         return;
233e45d8c71SBrad Bishop     }
234e45d8c71SBrad Bishop     iface->register_property(
2353013fb49SEd Tanous         name, value,
236e45d8c71SBrad Bishop         [&systemConfiguration,
237e45d8c71SBrad Bishop          jsonPointerString{std::string(jsonPointerString)}](
238e45d8c71SBrad Bishop             const PropertyType& newVal, PropertyType& val) {
239e45d8c71SBrad Bishop             val = newVal;
240b7077437SPatrick Williams             if (!setJsonFromPointer(jsonPointerString, val,
241b7077437SPatrick Williams                                     systemConfiguration))
242e45d8c71SBrad Bishop             {
243e45d8c71SBrad Bishop                 std::cerr << "error setting json field\n";
244e45d8c71SBrad Bishop                 return -1;
245e45d8c71SBrad Bishop             }
246e45d8c71SBrad Bishop             if (!writeJsonFiles(systemConfiguration))
247e45d8c71SBrad Bishop             {
248e45d8c71SBrad Bishop                 std::cerr << "error setting json file\n";
249e45d8c71SBrad Bishop                 return -1;
250e45d8c71SBrad Bishop             }
251e45d8c71SBrad Bishop             return 1;
252e45d8c71SBrad Bishop         });
253e45d8c71SBrad Bishop }
254e45d8c71SBrad Bishop 
createDeleteObjectMethod(const std::string & jsonPointerPath,const std::shared_ptr<sdbusplus::asio::dbus_interface> & iface,sdbusplus::asio::object_server & objServer,nlohmann::json & systemConfiguration)255e45d8c71SBrad Bishop void createDeleteObjectMethod(
256e45d8c71SBrad Bishop     const std::string& jsonPointerPath,
257e45d8c71SBrad Bishop     const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
258e45d8c71SBrad Bishop     sdbusplus::asio::object_server& objServer,
259e45d8c71SBrad Bishop     nlohmann::json& systemConfiguration)
260e45d8c71SBrad Bishop {
261e45d8c71SBrad Bishop     std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
262b7077437SPatrick Williams     iface->register_method(
263b7077437SPatrick Williams         "Delete", [&objServer, &systemConfiguration, interface,
264e45d8c71SBrad Bishop                    jsonPointerPath{std::string(jsonPointerPath)}]() {
265e45d8c71SBrad Bishop             std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
266e45d8c71SBrad Bishop                 interface.lock();
267e45d8c71SBrad Bishop             if (!dbusInterface)
268e45d8c71SBrad Bishop             {
269e45d8c71SBrad Bishop                 // this technically can't happen as the pointer is pointing to
270e45d8c71SBrad Bishop                 // us
271e45d8c71SBrad Bishop                 throw DBusInternalError();
272e45d8c71SBrad Bishop             }
273e45d8c71SBrad Bishop             nlohmann::json::json_pointer ptr(jsonPointerPath);
274e45d8c71SBrad Bishop             systemConfiguration[ptr] = nullptr;
275e45d8c71SBrad Bishop 
276e45d8c71SBrad Bishop             // todo(james): dig through sdbusplus to find out why we can't
277e45d8c71SBrad Bishop             // delete it in a method call
27849a888c2SEd Tanous             boost::asio::post(io, [&objServer, dbusInterface]() mutable {
279e45d8c71SBrad Bishop                 objServer.remove_interface(dbusInterface);
280e45d8c71SBrad Bishop             });
281e45d8c71SBrad Bishop 
282e45d8c71SBrad Bishop             if (!writeJsonFiles(systemConfiguration))
283e45d8c71SBrad Bishop             {
284e45d8c71SBrad Bishop                 std::cerr << "error setting json file\n";
285e45d8c71SBrad Bishop                 throw DBusInternalError();
286e45d8c71SBrad Bishop             }
287e45d8c71SBrad Bishop         });
288e45d8c71SBrad Bishop }
289e45d8c71SBrad Bishop 
290e45d8c71SBrad Bishop // adds simple json types to interface's properties
populateInterfaceFromJson(nlohmann::json & systemConfiguration,const std::string & jsonPointerPath,std::shared_ptr<sdbusplus::asio::dbus_interface> & iface,nlohmann::json & dict,sdbusplus::asio::object_server & objServer,sdbusplus::asio::PropertyPermission permission=sdbusplus::asio::PropertyPermission::readOnly)291e45d8c71SBrad Bishop void populateInterfaceFromJson(
292e45d8c71SBrad Bishop     nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
293e45d8c71SBrad Bishop     std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
294e45d8c71SBrad Bishop     nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
295e45d8c71SBrad Bishop     sdbusplus::asio::PropertyPermission permission =
296e45d8c71SBrad Bishop         sdbusplus::asio::PropertyPermission::readOnly)
297e45d8c71SBrad Bishop {
2982594d36eSPatrick Williams     for (const auto& [key, value] : dict.items())
299e45d8c71SBrad Bishop     {
300e45d8c71SBrad Bishop         auto type = value.type();
301e45d8c71SBrad Bishop         bool array = false;
302e45d8c71SBrad Bishop         if (value.type() == nlohmann::json::value_t::array)
303e45d8c71SBrad Bishop         {
304e45d8c71SBrad Bishop             array = true;
3053013fb49SEd Tanous             if (value.empty())
306e45d8c71SBrad Bishop             {
307e45d8c71SBrad Bishop                 continue;
308e45d8c71SBrad Bishop             }
309e45d8c71SBrad Bishop             type = value[0].type();
310e45d8c71SBrad Bishop             bool isLegal = true;
311e45d8c71SBrad Bishop             for (const auto& arrayItem : value)
312e45d8c71SBrad Bishop             {
313e45d8c71SBrad Bishop                 if (arrayItem.type() != type)
314e45d8c71SBrad Bishop                 {
315e45d8c71SBrad Bishop                     isLegal = false;
316e45d8c71SBrad Bishop                     break;
317e45d8c71SBrad Bishop                 }
318e45d8c71SBrad Bishop             }
319e45d8c71SBrad Bishop             if (!isLegal)
320e45d8c71SBrad Bishop             {
321e45d8c71SBrad Bishop                 std::cerr << "dbus format error" << value << "\n";
322e45d8c71SBrad Bishop                 continue;
323e45d8c71SBrad Bishop             }
324e45d8c71SBrad Bishop         }
325e45d8c71SBrad Bishop         if (type == nlohmann::json::value_t::object)
326e45d8c71SBrad Bishop         {
327e45d8c71SBrad Bishop             continue; // handled elsewhere
328e45d8c71SBrad Bishop         }
329e45d8c71SBrad Bishop 
330e45d8c71SBrad Bishop         std::string path = jsonPointerPath;
331e45d8c71SBrad Bishop         path.append("/").append(key);
332e45d8c71SBrad Bishop         if (permission == sdbusplus::asio::PropertyPermission::readWrite)
333e45d8c71SBrad Bishop         {
334e45d8c71SBrad Bishop             // all setable numbers are doubles as it is difficult to always
335e45d8c71SBrad Bishop             // create a configuration file with all whole numbers as decimals
336e45d8c71SBrad Bishop             // i.e. 1.0
337e45d8c71SBrad Bishop             if (array)
338e45d8c71SBrad Bishop             {
339e45d8c71SBrad Bishop                 if (value[0].is_number())
340e45d8c71SBrad Bishop                 {
341e45d8c71SBrad Bishop                     type = nlohmann::json::value_t::number_float;
342e45d8c71SBrad Bishop                 }
343e45d8c71SBrad Bishop             }
344e45d8c71SBrad Bishop             else if (value.is_number())
345e45d8c71SBrad Bishop             {
346e45d8c71SBrad Bishop                 type = nlohmann::json::value_t::number_float;
347e45d8c71SBrad Bishop             }
348e45d8c71SBrad Bishop         }
349e45d8c71SBrad Bishop 
350e45d8c71SBrad Bishop         switch (type)
351e45d8c71SBrad Bishop         {
352e45d8c71SBrad Bishop             case (nlohmann::json::value_t::boolean):
353e45d8c71SBrad Bishop             {
354e45d8c71SBrad Bishop                 if (array)
355e45d8c71SBrad Bishop                 {
356e45d8c71SBrad Bishop                     // todo: array of bool isn't detected correctly by
357e45d8c71SBrad Bishop                     // sdbusplus, change it to numbers
358e45d8c71SBrad Bishop                     addArrayToDbus<uint64_t>(key, value, iface.get(),
359e45d8c71SBrad Bishop                                              permission, systemConfiguration,
360e45d8c71SBrad Bishop                                              path);
361e45d8c71SBrad Bishop                 }
362e45d8c71SBrad Bishop 
363e45d8c71SBrad Bishop                 else
364e45d8c71SBrad Bishop                 {
365e45d8c71SBrad Bishop                     addProperty(key, value.get<bool>(), iface.get(),
366e45d8c71SBrad Bishop                                 systemConfiguration, path, permission);
367e45d8c71SBrad Bishop                 }
368e45d8c71SBrad Bishop                 break;
369e45d8c71SBrad Bishop             }
370e45d8c71SBrad Bishop             case (nlohmann::json::value_t::number_integer):
371e45d8c71SBrad Bishop             {
372e45d8c71SBrad Bishop                 if (array)
373e45d8c71SBrad Bishop                 {
374e45d8c71SBrad Bishop                     addArrayToDbus<int64_t>(key, value, iface.get(), permission,
375e45d8c71SBrad Bishop                                             systemConfiguration, path);
376e45d8c71SBrad Bishop                 }
377e45d8c71SBrad Bishop                 else
378e45d8c71SBrad Bishop                 {
379e45d8c71SBrad Bishop                     addProperty(key, value.get<int64_t>(), iface.get(),
380e45d8c71SBrad Bishop                                 systemConfiguration, path,
381e45d8c71SBrad Bishop                                 sdbusplus::asio::PropertyPermission::readOnly);
382e45d8c71SBrad Bishop                 }
383e45d8c71SBrad Bishop                 break;
384e45d8c71SBrad Bishop             }
385e45d8c71SBrad Bishop             case (nlohmann::json::value_t::number_unsigned):
386e45d8c71SBrad Bishop             {
387e45d8c71SBrad Bishop                 if (array)
388e45d8c71SBrad Bishop                 {
389e45d8c71SBrad Bishop                     addArrayToDbus<uint64_t>(key, value, iface.get(),
390e45d8c71SBrad Bishop                                              permission, systemConfiguration,
391e45d8c71SBrad Bishop                                              path);
392e45d8c71SBrad Bishop                 }
393e45d8c71SBrad Bishop                 else
394e45d8c71SBrad Bishop                 {
395e45d8c71SBrad Bishop                     addProperty(key, value.get<uint64_t>(), iface.get(),
396e45d8c71SBrad Bishop                                 systemConfiguration, path,
397e45d8c71SBrad Bishop                                 sdbusplus::asio::PropertyPermission::readOnly);
398e45d8c71SBrad Bishop                 }
399e45d8c71SBrad Bishop                 break;
400e45d8c71SBrad Bishop             }
401e45d8c71SBrad Bishop             case (nlohmann::json::value_t::number_float):
402e45d8c71SBrad Bishop             {
403e45d8c71SBrad Bishop                 if (array)
404e45d8c71SBrad Bishop                 {
405e45d8c71SBrad Bishop                     addArrayToDbus<double>(key, value, iface.get(), permission,
406e45d8c71SBrad Bishop                                            systemConfiguration, path);
407e45d8c71SBrad Bishop                 }
408e45d8c71SBrad Bishop 
409e45d8c71SBrad Bishop                 else
410e45d8c71SBrad Bishop                 {
411e45d8c71SBrad Bishop                     addProperty(key, value.get<double>(), iface.get(),
412e45d8c71SBrad Bishop                                 systemConfiguration, path, permission);
413e45d8c71SBrad Bishop                 }
414e45d8c71SBrad Bishop                 break;
415e45d8c71SBrad Bishop             }
416e45d8c71SBrad Bishop             case (nlohmann::json::value_t::string):
417e45d8c71SBrad Bishop             {
418e45d8c71SBrad Bishop                 if (array)
419e45d8c71SBrad Bishop                 {
420e45d8c71SBrad Bishop                     addArrayToDbus<std::string>(key, value, iface.get(),
421e45d8c71SBrad Bishop                                                 permission, systemConfiguration,
422e45d8c71SBrad Bishop                                                 path);
423e45d8c71SBrad Bishop                 }
424e45d8c71SBrad Bishop                 else
425e45d8c71SBrad Bishop                 {
426e45d8c71SBrad Bishop                     addProperty(key, value.get<std::string>(), iface.get(),
427e45d8c71SBrad Bishop                                 systemConfiguration, path, permission);
428e45d8c71SBrad Bishop                 }
429e45d8c71SBrad Bishop                 break;
430e45d8c71SBrad Bishop             }
431e45d8c71SBrad Bishop             default:
432e45d8c71SBrad Bishop             {
433e45d8c71SBrad Bishop                 std::cerr << "Unexpected json type in system configuration "
434e45d8c71SBrad Bishop                           << key << ": " << value.type_name() << "\n";
435e45d8c71SBrad Bishop                 break;
436e45d8c71SBrad Bishop             }
437e45d8c71SBrad Bishop         }
438e45d8c71SBrad Bishop     }
439e45d8c71SBrad Bishop     if (permission == sdbusplus::asio::PropertyPermission::readWrite)
440e45d8c71SBrad Bishop     {
441e45d8c71SBrad Bishop         createDeleteObjectMethod(jsonPointerPath, iface, objServer,
442e45d8c71SBrad Bishop                                  systemConfiguration);
443e45d8c71SBrad Bishop     }
444d97c6319SJohn Edward Broadbent     tryIfaceInitialize(iface);
445e45d8c71SBrad Bishop }
446e45d8c71SBrad Bishop 
getPermission(const std::string & interface)447e45d8c71SBrad Bishop sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
448e45d8c71SBrad Bishop {
449e45d8c71SBrad Bishop     return std::find(settableInterfaces.begin(), settableInterfaces.end(),
450e45d8c71SBrad Bishop                      interface) != settableInterfaces.end()
451e45d8c71SBrad Bishop                ? sdbusplus::asio::PropertyPermission::readWrite
452e45d8c71SBrad Bishop                : sdbusplus::asio::PropertyPermission::readOnly;
453e45d8c71SBrad Bishop }
454e45d8c71SBrad Bishop 
createAddObjectMethod(const std::string & jsonPointerPath,const std::string & path,nlohmann::json & systemConfiguration,sdbusplus::asio::object_server & objServer,const std::string & board)455b7077437SPatrick Williams void createAddObjectMethod(
456b7077437SPatrick Williams     const std::string& jsonPointerPath, const std::string& path,
457e45d8c71SBrad Bishop     nlohmann::json& systemConfiguration,
458b7077437SPatrick Williams     sdbusplus::asio::object_server& objServer, const std::string& board)
459e45d8c71SBrad Bishop {
460e45d8c71SBrad Bishop     std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
461e45d8c71SBrad Bishop         objServer, path, "xyz.openbmc_project.AddObject", board);
462e45d8c71SBrad Bishop 
463e45d8c71SBrad Bishop     iface->register_method(
464e45d8c71SBrad Bishop         "AddObject",
465e45d8c71SBrad Bishop         [&systemConfiguration, &objServer,
466e45d8c71SBrad Bishop          jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
467e45d8c71SBrad Bishop          board](const boost::container::flat_map<std::string, JsonVariantType>&
468e45d8c71SBrad Bishop                     data) {
469e45d8c71SBrad Bishop             nlohmann::json::json_pointer ptr(jsonPointerPath);
470e45d8c71SBrad Bishop             nlohmann::json& base = systemConfiguration[ptr];
471e45d8c71SBrad Bishop             auto findExposes = base.find("Exposes");
472e45d8c71SBrad Bishop 
473e45d8c71SBrad Bishop             if (findExposes == base.end())
474e45d8c71SBrad Bishop             {
475e45d8c71SBrad Bishop                 throw std::invalid_argument("Entity must have children.");
476e45d8c71SBrad Bishop             }
477e45d8c71SBrad Bishop 
478e45d8c71SBrad Bishop             // this will throw invalid-argument to sdbusplus if invalid json
479e45d8c71SBrad Bishop             nlohmann::json newData{};
480e45d8c71SBrad Bishop             for (const auto& item : data)
481e45d8c71SBrad Bishop             {
482e45d8c71SBrad Bishop                 nlohmann::json& newJson = newData[item.first];
4833013fb49SEd Tanous                 std::visit(
4843013fb49SEd Tanous                     [&newJson](auto&& val) {
4853013fb49SEd Tanous                         newJson = std::forward<decltype(val)>(val);
4863013fb49SEd Tanous                     },
487e45d8c71SBrad Bishop                     item.second);
488e45d8c71SBrad Bishop             }
489e45d8c71SBrad Bishop 
490e45d8c71SBrad Bishop             auto findName = newData.find("Name");
491e45d8c71SBrad Bishop             auto findType = newData.find("Type");
492e45d8c71SBrad Bishop             if (findName == newData.end() || findType == newData.end())
493e45d8c71SBrad Bishop             {
494e45d8c71SBrad Bishop                 throw std::invalid_argument("AddObject missing Name or Type");
495e45d8c71SBrad Bishop             }
496e45d8c71SBrad Bishop             const std::string* type = findType->get_ptr<const std::string*>();
497e45d8c71SBrad Bishop             const std::string* name = findName->get_ptr<const std::string*>();
498e45d8c71SBrad Bishop             if (type == nullptr || name == nullptr)
499e45d8c71SBrad Bishop             {
500e45d8c71SBrad Bishop                 throw std::invalid_argument("Type and Name must be a string.");
501e45d8c71SBrad Bishop             }
502e45d8c71SBrad Bishop 
503e45d8c71SBrad Bishop             bool foundNull = false;
504e45d8c71SBrad Bishop             size_t lastIndex = 0;
505e45d8c71SBrad Bishop             // we add in the "exposes"
506e45d8c71SBrad Bishop             for (const auto& expose : *findExposes)
507e45d8c71SBrad Bishop             {
508e45d8c71SBrad Bishop                 if (expose.is_null())
509e45d8c71SBrad Bishop                 {
510e45d8c71SBrad Bishop                     foundNull = true;
511e45d8c71SBrad Bishop                     continue;
512e45d8c71SBrad Bishop                 }
513e45d8c71SBrad Bishop 
514e45d8c71SBrad Bishop                 if (expose["Name"] == *name && expose["Type"] == *type)
515e45d8c71SBrad Bishop                 {
516e45d8c71SBrad Bishop                     throw std::invalid_argument(
517e45d8c71SBrad Bishop                         "Field already in JSON, not adding");
518e45d8c71SBrad Bishop                 }
519e45d8c71SBrad Bishop 
520e45d8c71SBrad Bishop                 if (foundNull)
521e45d8c71SBrad Bishop                 {
522e45d8c71SBrad Bishop                     continue;
523e45d8c71SBrad Bishop                 }
524e45d8c71SBrad Bishop 
525e45d8c71SBrad Bishop                 lastIndex++;
526e45d8c71SBrad Bishop             }
527e45d8c71SBrad Bishop 
528e45d8c71SBrad Bishop             std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
529b068d431SPotin Lai                                      boost::to_lower_copy(*type) + ".json");
530e45d8c71SBrad Bishop             // todo(james) we might want to also make a list of 'can add'
531e45d8c71SBrad Bishop             // interfaces but for now I think the assumption if there is a
532e45d8c71SBrad Bishop             // schema avaliable that it is allowed to update is fine
533e45d8c71SBrad Bishop             if (!schemaFile.good())
534e45d8c71SBrad Bishop             {
535e45d8c71SBrad Bishop                 throw std::invalid_argument(
536e45d8c71SBrad Bishop                     "No schema avaliable, cannot validate.");
537e45d8c71SBrad Bishop             }
538b7077437SPatrick Williams             nlohmann::json schema =
539b7077437SPatrick Williams                 nlohmann::json::parse(schemaFile, nullptr, false, true);
540e45d8c71SBrad Bishop             if (schema.is_discarded())
541e45d8c71SBrad Bishop             {
542e45d8c71SBrad Bishop                 std::cerr << "Schema not legal" << *type << ".json\n";
543e45d8c71SBrad Bishop                 throw DBusInternalError();
544e45d8c71SBrad Bishop             }
545e45d8c71SBrad Bishop             if (!validateJson(schema, newData))
546e45d8c71SBrad Bishop             {
547e45d8c71SBrad Bishop                 throw std::invalid_argument("Data does not match schema");
548e45d8c71SBrad Bishop             }
549e45d8c71SBrad Bishop             if (foundNull)
550e45d8c71SBrad Bishop             {
551e45d8c71SBrad Bishop                 findExposes->at(lastIndex) = newData;
552e45d8c71SBrad Bishop             }
553e45d8c71SBrad Bishop             else
554e45d8c71SBrad Bishop             {
555e45d8c71SBrad Bishop                 findExposes->push_back(newData);
556e45d8c71SBrad Bishop             }
557e45d8c71SBrad Bishop             if (!writeJsonFiles(systemConfiguration))
558e45d8c71SBrad Bishop             {
559e45d8c71SBrad Bishop                 std::cerr << "Error writing json files\n";
560e45d8c71SBrad Bishop                 throw DBusInternalError();
561e45d8c71SBrad Bishop             }
562e45d8c71SBrad Bishop             std::string dbusName = *name;
563e45d8c71SBrad Bishop 
564b7077437SPatrick Williams             std::regex_replace(dbusName.begin(), dbusName.begin(),
565b7077437SPatrick Williams                                dbusName.end(), illegalDbusMemberRegex, "_");
566e45d8c71SBrad Bishop 
567e45d8c71SBrad Bishop             std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
568e45d8c71SBrad Bishop                 createInterface(objServer, path + "/" + dbusName,
569b7077437SPatrick Williams                                 "xyz.openbmc_project.Configuration." + *type,
570b7077437SPatrick Williams                                 board, true);
571e45d8c71SBrad Bishop             // permission is read-write, as since we just created it, must be
572e45d8c71SBrad Bishop             // runtime modifiable
573e45d8c71SBrad Bishop             populateInterfaceFromJson(
574e45d8c71SBrad Bishop                 systemConfiguration,
575e45d8c71SBrad Bishop                 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
576e45d8c71SBrad Bishop                 interface, newData, objServer,
577e45d8c71SBrad Bishop                 sdbusplus::asio::PropertyPermission::readWrite);
578e45d8c71SBrad Bishop         });
579d97c6319SJohn Edward Broadbent     tryIfaceInitialize(iface);
580e45d8c71SBrad Bishop }
581e45d8c71SBrad Bishop 
postToDbus(const nlohmann::json & newConfiguration,nlohmann::json & systemConfiguration,sdbusplus::asio::object_server & objServer)582e45d8c71SBrad Bishop void postToDbus(const nlohmann::json& newConfiguration,
583e45d8c71SBrad Bishop                 nlohmann::json& systemConfiguration,
584e45d8c71SBrad Bishop                 sdbusplus::asio::object_server& objServer)
585e45d8c71SBrad Bishop 
586e45d8c71SBrad Bishop {
5876eb60972SMatt Spinler     std::map<std::string, std::string> newBoards; // path -> name
588ca2eb04eSBenjamin Fair 
589e45d8c71SBrad Bishop     // iterate through boards
5902594d36eSPatrick Williams     for (const auto& [boardId, boardConfig] : newConfiguration.items())
591e45d8c71SBrad Bishop     {
5923d1909aeSMatt Spinler         std::string boardName = boardConfig["Name"];
5933d1909aeSMatt Spinler         std::string boardNameOrig = boardConfig["Name"];
594e45d8c71SBrad Bishop         std::string jsonPointerPath = "/" + boardId;
595e45d8c71SBrad Bishop         // loop through newConfiguration, but use values from system
596e45d8c71SBrad Bishop         // configuration to be able to modify via dbus later
597e45d8c71SBrad Bishop         auto boardValues = systemConfiguration[boardId];
598e45d8c71SBrad Bishop         auto findBoardType = boardValues.find("Type");
599e45d8c71SBrad Bishop         std::string boardType;
600e45d8c71SBrad Bishop         if (findBoardType != boardValues.end() &&
601e45d8c71SBrad Bishop             findBoardType->type() == nlohmann::json::value_t::string)
602e45d8c71SBrad Bishop         {
603e45d8c71SBrad Bishop             boardType = findBoardType->get<std::string>();
604e45d8c71SBrad Bishop             std::regex_replace(boardType.begin(), boardType.begin(),
605e45d8c71SBrad Bishop                                boardType.end(), illegalDbusMemberRegex, "_");
606e45d8c71SBrad Bishop         }
607e45d8c71SBrad Bishop         else
608e45d8c71SBrad Bishop         {
6093d1909aeSMatt Spinler             std::cerr << "Unable to find type for " << boardName
610e45d8c71SBrad Bishop                       << " reverting to Chassis.\n";
611e45d8c71SBrad Bishop             boardType = "Chassis";
612e45d8c71SBrad Bishop         }
613e45d8c71SBrad Bishop         std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
614e45d8c71SBrad Bishop 
6153d1909aeSMatt Spinler         std::regex_replace(boardName.begin(), boardName.begin(),
6163d1909aeSMatt Spinler                            boardName.end(), illegalDbusMemberRegex, "_");
6173d1909aeSMatt Spinler         std::string boardPath = "/xyz/openbmc_project/inventory/system/";
6183d1909aeSMatt Spinler         boardPath += boardtypeLower;
6193d1909aeSMatt Spinler         boardPath += "/";
6203d1909aeSMatt Spinler         boardPath += boardName;
621e45d8c71SBrad Bishop 
622e45d8c71SBrad Bishop         std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
6233d1909aeSMatt Spinler             createInterface(objServer, boardPath,
6243d1909aeSMatt Spinler                             "xyz.openbmc_project.Inventory.Item", boardName);
625e45d8c71SBrad Bishop 
626e45d8c71SBrad Bishop         std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
6273d1909aeSMatt Spinler             createInterface(objServer, boardPath,
628e45d8c71SBrad Bishop                             "xyz.openbmc_project.Inventory.Item." + boardType,
6293d1909aeSMatt Spinler                             boardNameOrig);
630e45d8c71SBrad Bishop 
6313d1909aeSMatt Spinler         createAddObjectMethod(jsonPointerPath, boardPath, systemConfiguration,
6323d1909aeSMatt Spinler                               objServer, boardNameOrig);
633e45d8c71SBrad Bishop 
634e45d8c71SBrad Bishop         populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
635e45d8c71SBrad Bishop                                   boardIface, boardValues, objServer);
636e45d8c71SBrad Bishop         jsonPointerPath += "/";
637e45d8c71SBrad Bishop         // iterate through board properties
6382594d36eSPatrick Williams         for (const auto& [propName, propValue] : boardValues.items())
639e45d8c71SBrad Bishop         {
640e45d8c71SBrad Bishop             if (propValue.type() == nlohmann::json::value_t::object)
641e45d8c71SBrad Bishop             {
642e45d8c71SBrad Bishop                 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
6433d1909aeSMatt Spinler                     createInterface(objServer, boardPath, propName,
6443d1909aeSMatt Spinler                                     boardNameOrig);
645e45d8c71SBrad Bishop 
646e45d8c71SBrad Bishop                 populateInterfaceFromJson(systemConfiguration,
647e45d8c71SBrad Bishop                                           jsonPointerPath + propName, iface,
648e45d8c71SBrad Bishop                                           propValue, objServer);
649e45d8c71SBrad Bishop             }
650e45d8c71SBrad Bishop         }
651e45d8c71SBrad Bishop 
652e45d8c71SBrad Bishop         auto exposes = boardValues.find("Exposes");
653e45d8c71SBrad Bishop         if (exposes == boardValues.end())
654e45d8c71SBrad Bishop         {
655e45d8c71SBrad Bishop             continue;
656e45d8c71SBrad Bishop         }
657e45d8c71SBrad Bishop         // iterate through exposes
658e45d8c71SBrad Bishop         jsonPointerPath += "Exposes/";
659e45d8c71SBrad Bishop 
660e45d8c71SBrad Bishop         // store the board level pointer so we can modify it on the way down
661e45d8c71SBrad Bishop         std::string jsonPointerPathBoard = jsonPointerPath;
662e45d8c71SBrad Bishop         size_t exposesIndex = -1;
663e45d8c71SBrad Bishop         for (auto& item : *exposes)
664e45d8c71SBrad Bishop         {
665e45d8c71SBrad Bishop             exposesIndex++;
666e45d8c71SBrad Bishop             jsonPointerPath = jsonPointerPathBoard;
667e45d8c71SBrad Bishop             jsonPointerPath += std::to_string(exposesIndex);
668e45d8c71SBrad Bishop 
669e45d8c71SBrad Bishop             auto findName = item.find("Name");
670e45d8c71SBrad Bishop             if (findName == item.end())
671e45d8c71SBrad Bishop             {
672e45d8c71SBrad Bishop                 std::cerr << "cannot find name in field " << item << "\n";
673e45d8c71SBrad Bishop                 continue;
674e45d8c71SBrad Bishop             }
675e45d8c71SBrad Bishop             auto findStatus = item.find("Status");
676e45d8c71SBrad Bishop             // if status is not found it is assumed to be status = 'okay'
677e45d8c71SBrad Bishop             if (findStatus != item.end())
678e45d8c71SBrad Bishop             {
679e45d8c71SBrad Bishop                 if (*findStatus == "disabled")
680e45d8c71SBrad Bishop                 {
681e45d8c71SBrad Bishop                     continue;
682e45d8c71SBrad Bishop                 }
683e45d8c71SBrad Bishop             }
684e45d8c71SBrad Bishop             auto findType = item.find("Type");
685e45d8c71SBrad Bishop             std::string itemType;
686e45d8c71SBrad Bishop             if (findType != item.end())
687e45d8c71SBrad Bishop             {
688e45d8c71SBrad Bishop                 itemType = findType->get<std::string>();
689e45d8c71SBrad Bishop                 std::regex_replace(itemType.begin(), itemType.begin(),
690e45d8c71SBrad Bishop                                    itemType.end(), illegalDbusPathRegex, "_");
691e45d8c71SBrad Bishop             }
692e45d8c71SBrad Bishop             else
693e45d8c71SBrad Bishop             {
694e45d8c71SBrad Bishop                 itemType = "unknown";
695e45d8c71SBrad Bishop             }
696e45d8c71SBrad Bishop             std::string itemName = findName->get<std::string>();
697e45d8c71SBrad Bishop             std::regex_replace(itemName.begin(), itemName.begin(),
698e45d8c71SBrad Bishop                                itemName.end(), illegalDbusMemberRegex, "_");
6993d1909aeSMatt Spinler             std::string ifacePath = boardPath;
700e45d8c71SBrad Bishop             ifacePath += "/";
701e45d8c71SBrad Bishop             ifacePath += itemName;
702e45d8c71SBrad Bishop 
70374ebe59cSSui Chen             if (itemType == "BMC")
70474ebe59cSSui Chen             {
70574ebe59cSSui Chen                 std::shared_ptr<sdbusplus::asio::dbus_interface> bmcIface =
70674ebe59cSSui Chen                     createInterface(objServer, ifacePath,
70774ebe59cSSui Chen                                     "xyz.openbmc_project.Inventory.Item.Bmc",
7083d1909aeSMatt Spinler                                     boardNameOrig);
70974ebe59cSSui Chen                 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
71074ebe59cSSui Chen                                           bmcIface, item, objServer,
71174ebe59cSSui Chen                                           getPermission(itemType));
71274ebe59cSSui Chen             }
713eb587b49SEdward Lee             else if (itemType == "System")
714eb587b49SEdward Lee             {
715eb587b49SEdward Lee                 std::shared_ptr<sdbusplus::asio::dbus_interface> systemIface =
716eb587b49SEdward Lee                     createInterface(objServer, ifacePath,
717eb587b49SEdward Lee                                     "xyz.openbmc_project.Inventory.Item.System",
7183d1909aeSMatt Spinler                                     boardNameOrig);
719eb587b49SEdward Lee                 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
720eb587b49SEdward Lee                                           systemIface, item, objServer,
721eb587b49SEdward Lee                                           getPermission(itemType));
722eb587b49SEdward Lee             }
72374ebe59cSSui Chen 
7242594d36eSPatrick Williams             for (const auto& [name, config] : item.items())
725e45d8c71SBrad Bishop             {
726e45d8c71SBrad Bishop                 jsonPointerPath = jsonPointerPathBoard;
727e45d8c71SBrad Bishop                 jsonPointerPath.append(std::to_string(exposesIndex))
728e45d8c71SBrad Bishop                     .append("/")
729e45d8c71SBrad Bishop                     .append(name);
730e45d8c71SBrad Bishop                 if (config.type() == nlohmann::json::value_t::object)
731e45d8c71SBrad Bishop                 {
732e45d8c71SBrad Bishop                     std::string ifaceName =
733e45d8c71SBrad Bishop                         "xyz.openbmc_project.Configuration.";
734e45d8c71SBrad Bishop                     ifaceName.append(itemType).append(".").append(name);
735e45d8c71SBrad Bishop 
736e45d8c71SBrad Bishop                     std::shared_ptr<sdbusplus::asio::dbus_interface>
737e45d8c71SBrad Bishop                         objectIface = createInterface(objServer, ifacePath,
7383d1909aeSMatt Spinler                                                       ifaceName, boardNameOrig);
739e45d8c71SBrad Bishop 
740e45d8c71SBrad Bishop                     populateInterfaceFromJson(
741e45d8c71SBrad Bishop                         systemConfiguration, jsonPointerPath, objectIface,
742e45d8c71SBrad Bishop                         config, objServer, getPermission(name));
743e45d8c71SBrad Bishop                 }
744e45d8c71SBrad Bishop                 else if (config.type() == nlohmann::json::value_t::array)
745e45d8c71SBrad Bishop                 {
746e45d8c71SBrad Bishop                     size_t index = 0;
7473013fb49SEd Tanous                     if (config.empty())
748e45d8c71SBrad Bishop                     {
749e45d8c71SBrad Bishop                         continue;
750e45d8c71SBrad Bishop                     }
751e45d8c71SBrad Bishop                     bool isLegal = true;
752e45d8c71SBrad Bishop                     auto type = config[0].type();
753e45d8c71SBrad Bishop                     if (type != nlohmann::json::value_t::object)
754e45d8c71SBrad Bishop                     {
755e45d8c71SBrad Bishop                         continue;
756e45d8c71SBrad Bishop                     }
757e45d8c71SBrad Bishop 
758e45d8c71SBrad Bishop                     // verify legal json
759e45d8c71SBrad Bishop                     for (const auto& arrayItem : config)
760e45d8c71SBrad Bishop                     {
761e45d8c71SBrad Bishop                         if (arrayItem.type() != type)
762e45d8c71SBrad Bishop                         {
763e45d8c71SBrad Bishop                             isLegal = false;
764e45d8c71SBrad Bishop                             break;
765e45d8c71SBrad Bishop                         }
766e45d8c71SBrad Bishop                     }
767e45d8c71SBrad Bishop                     if (!isLegal)
768e45d8c71SBrad Bishop                     {
769e45d8c71SBrad Bishop                         std::cerr << "dbus format error" << config << "\n";
770e45d8c71SBrad Bishop                         break;
771e45d8c71SBrad Bishop                     }
772e45d8c71SBrad Bishop 
773e45d8c71SBrad Bishop                     for (auto& arrayItem : config)
774e45d8c71SBrad Bishop                     {
775e45d8c71SBrad Bishop                         std::string ifaceName =
776e45d8c71SBrad Bishop                             "xyz.openbmc_project.Configuration.";
777e45d8c71SBrad Bishop                         ifaceName.append(itemType).append(".").append(name);
778e45d8c71SBrad Bishop                         ifaceName.append(std::to_string(index));
779e45d8c71SBrad Bishop 
780e45d8c71SBrad Bishop                         std::shared_ptr<sdbusplus::asio::dbus_interface>
781e45d8c71SBrad Bishop                             objectIface = createInterface(
7823d1909aeSMatt Spinler                                 objServer, ifacePath, ifaceName, boardNameOrig);
783e45d8c71SBrad Bishop 
784e45d8c71SBrad Bishop                         populateInterfaceFromJson(
785e45d8c71SBrad Bishop                             systemConfiguration,
786e45d8c71SBrad Bishop                             jsonPointerPath + "/" + std::to_string(index),
787e45d8c71SBrad Bishop                             objectIface, arrayItem, objServer,
788e45d8c71SBrad Bishop                             getPermission(name));
789e45d8c71SBrad Bishop                         index++;
790e45d8c71SBrad Bishop                     }
791e45d8c71SBrad Bishop                 }
792e45d8c71SBrad Bishop             }
793ca2eb04eSBenjamin Fair 
794*5c1a61a8SGeorge Liu             std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
795*5c1a61a8SGeorge Liu                 createInterface(objServer, ifacePath,
796*5c1a61a8SGeorge Liu                                 "xyz.openbmc_project.Configuration." + itemType,
797*5c1a61a8SGeorge Liu                                 boardNameOrig);
798*5c1a61a8SGeorge Liu 
799*5c1a61a8SGeorge Liu             populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
800*5c1a61a8SGeorge Liu                                       itemIface, item, objServer,
801*5c1a61a8SGeorge Liu                                       getPermission(itemType));
802*5c1a61a8SGeorge Liu 
8036eb60972SMatt Spinler             topology.addBoard(boardPath, boardType, boardNameOrig, item);
804e45d8c71SBrad Bishop         }
805ca2eb04eSBenjamin Fair 
8066eb60972SMatt Spinler         newBoards.emplace(boardPath, boardNameOrig);
8076eb60972SMatt Spinler     }
8086eb60972SMatt Spinler 
8096eb60972SMatt Spinler     for (const auto& [assocPath, assocPropValue] :
8106eb60972SMatt Spinler          topology.getAssocs(newBoards))
811ca2eb04eSBenjamin Fair     {
8126eb60972SMatt Spinler         auto findBoard = newBoards.find(assocPath);
8136eb60972SMatt Spinler         if (findBoard == newBoards.end())
8146eb60972SMatt Spinler         {
8156eb60972SMatt Spinler             continue;
8166eb60972SMatt Spinler         }
817ca2eb04eSBenjamin Fair 
8186eb60972SMatt Spinler         auto ifacePtr = createInterface(
8196eb60972SMatt Spinler             objServer, assocPath, "xyz.openbmc_project.Association.Definitions",
8206eb60972SMatt Spinler             findBoard->second);
8216eb60972SMatt Spinler 
8226eb60972SMatt Spinler         ifacePtr->register_property("Associations", assocPropValue);
823d97c6319SJohn Edward Broadbent         tryIfaceInitialize(ifacePtr);
824ca2eb04eSBenjamin Fair     }
825e45d8c71SBrad Bishop }
826e45d8c71SBrad Bishop 
827e45d8c71SBrad Bishop // reads json files out of the filesystem
loadConfigurations(std::list<nlohmann::json> & configurations)828e45d8c71SBrad Bishop bool loadConfigurations(std::list<nlohmann::json>& configurations)
829e45d8c71SBrad Bishop {
830e45d8c71SBrad Bishop     // find configuration files
831e45d8c71SBrad Bishop     std::vector<std::filesystem::path> jsonPaths;
832e45d8c71SBrad Bishop     if (!findFiles(
833e45d8c71SBrad Bishop             std::vector<std::filesystem::path>{configurationDirectory,
834e45d8c71SBrad Bishop                                                hostConfigurationDirectory},
835e45d8c71SBrad Bishop             R"(.*\.json)", jsonPaths))
836e45d8c71SBrad Bishop     {
837e45d8c71SBrad Bishop         std::cerr << "Unable to find any configuration files in "
838e45d8c71SBrad Bishop                   << configurationDirectory << "\n";
839e45d8c71SBrad Bishop         return false;
840e45d8c71SBrad Bishop     }
841e45d8c71SBrad Bishop 
842b7077437SPatrick Williams     std::ifstream schemaStream(
843b7077437SPatrick Williams         std::string(schemaDirectory) + "/" + globalSchema);
844e45d8c71SBrad Bishop     if (!schemaStream.good())
845e45d8c71SBrad Bishop     {
846e45d8c71SBrad Bishop         std::cerr
847e45d8c71SBrad Bishop             << "Cannot open schema file,  cannot validate JSON, exiting\n\n";
848e45d8c71SBrad Bishop         std::exit(EXIT_FAILURE);
849e45d8c71SBrad Bishop         return false;
850e45d8c71SBrad Bishop     }
851b7077437SPatrick Williams     nlohmann::json schema =
852b7077437SPatrick Williams         nlohmann::json::parse(schemaStream, nullptr, false, true);
853e45d8c71SBrad Bishop     if (schema.is_discarded())
854e45d8c71SBrad Bishop     {
855e45d8c71SBrad Bishop         std::cerr
856e45d8c71SBrad Bishop             << "Illegal schema file detected, cannot validate JSON, exiting\n";
857e45d8c71SBrad Bishop         std::exit(EXIT_FAILURE);
858e45d8c71SBrad Bishop         return false;
859e45d8c71SBrad Bishop     }
860e45d8c71SBrad Bishop 
861e45d8c71SBrad Bishop     for (auto& jsonPath : jsonPaths)
862e45d8c71SBrad Bishop     {
863e45d8c71SBrad Bishop         std::ifstream jsonStream(jsonPath.c_str());
864e45d8c71SBrad Bishop         if (!jsonStream.good())
865e45d8c71SBrad Bishop         {
866e45d8c71SBrad Bishop             std::cerr << "unable to open " << jsonPath.string() << "\n";
867e45d8c71SBrad Bishop             continue;
868e45d8c71SBrad Bishop         }
8690f3a4d99SPotin Lai         auto data = nlohmann::json::parse(jsonStream, nullptr, false, true);
870e45d8c71SBrad Bishop         if (data.is_discarded())
871e45d8c71SBrad Bishop         {
872e45d8c71SBrad Bishop             std::cerr << "syntax error in " << jsonPath.string() << "\n";
873e45d8c71SBrad Bishop             continue;
874e45d8c71SBrad Bishop         }
875e45d8c71SBrad Bishop         /*
876e45d8c71SBrad Bishop          * todo(james): reenable this once less things are in flight
877e45d8c71SBrad Bishop          *
878e45d8c71SBrad Bishop         if (!validateJson(schema, data))
879e45d8c71SBrad Bishop         {
880e45d8c71SBrad Bishop             std::cerr << "Error validating " << jsonPath.string() << "\n";
881e45d8c71SBrad Bishop             continue;
882e45d8c71SBrad Bishop         }
883e45d8c71SBrad Bishop         */
884e45d8c71SBrad Bishop 
885e45d8c71SBrad Bishop         if (data.type() == nlohmann::json::value_t::array)
886e45d8c71SBrad Bishop         {
887e45d8c71SBrad Bishop             for (auto& d : data)
888e45d8c71SBrad Bishop             {
889e45d8c71SBrad Bishop                 configurations.emplace_back(d);
890e45d8c71SBrad Bishop             }
891e45d8c71SBrad Bishop         }
892e45d8c71SBrad Bishop         else
893e45d8c71SBrad Bishop         {
894e45d8c71SBrad Bishop             configurations.emplace_back(data);
895e45d8c71SBrad Bishop         }
896e45d8c71SBrad Bishop     }
897e45d8c71SBrad Bishop     return true;
898e45d8c71SBrad Bishop }
899e45d8c71SBrad Bishop 
deviceRequiresPowerOn(const nlohmann::json & entity)900e45d8c71SBrad Bishop static bool deviceRequiresPowerOn(const nlohmann::json& entity)
901e45d8c71SBrad Bishop {
902e45d8c71SBrad Bishop     auto powerState = entity.find("PowerState");
903e45d8c71SBrad Bishop     if (powerState == entity.end())
904e45d8c71SBrad Bishop     {
905e45d8c71SBrad Bishop         return false;
906e45d8c71SBrad Bishop     }
907e45d8c71SBrad Bishop 
9083013fb49SEd Tanous     const auto* ptr = powerState->get_ptr<const std::string*>();
9093013fb49SEd Tanous     if (ptr == nullptr)
910e45d8c71SBrad Bishop     {
911e45d8c71SBrad Bishop         return false;
912e45d8c71SBrad Bishop     }
913e45d8c71SBrad Bishop 
914e45d8c71SBrad Bishop     return *ptr == "On" || *ptr == "BiosPost";
915e45d8c71SBrad Bishop }
916e45d8c71SBrad Bishop 
pruneDevice(const nlohmann::json & systemConfiguration,const bool powerOff,const bool scannedPowerOff,const std::string & name,const nlohmann::json & device)917e45d8c71SBrad Bishop static void pruneDevice(const nlohmann::json& systemConfiguration,
918e45d8c71SBrad Bishop                         const bool powerOff, const bool scannedPowerOff,
919e45d8c71SBrad Bishop                         const std::string& name, const nlohmann::json& device)
920e45d8c71SBrad Bishop {
921e45d8c71SBrad Bishop     if (systemConfiguration.contains(name))
922e45d8c71SBrad Bishop     {
923e45d8c71SBrad Bishop         return;
924e45d8c71SBrad Bishop     }
925e45d8c71SBrad Bishop 
926e45d8c71SBrad Bishop     if (deviceRequiresPowerOn(device) && (powerOff || scannedPowerOff))
927e45d8c71SBrad Bishop     {
928e45d8c71SBrad Bishop         return;
929e45d8c71SBrad Bishop     }
930e45d8c71SBrad Bishop 
931e45d8c71SBrad Bishop     logDeviceRemoved(device);
932e45d8c71SBrad Bishop }
933e45d8c71SBrad Bishop 
startRemovedTimer(boost::asio::steady_timer & timer,nlohmann::json & systemConfiguration)934e45d8c71SBrad Bishop void startRemovedTimer(boost::asio::steady_timer& timer,
935e45d8c71SBrad Bishop                        nlohmann::json& systemConfiguration)
936e45d8c71SBrad Bishop {
937e45d8c71SBrad Bishop     static bool scannedPowerOff = false;
938e45d8c71SBrad Bishop     static bool scannedPowerOn = false;
939e45d8c71SBrad Bishop 
940e45d8c71SBrad Bishop     if (systemConfiguration.empty() || lastJson.empty())
941e45d8c71SBrad Bishop     {
942e45d8c71SBrad Bishop         return; // not ready yet
943e45d8c71SBrad Bishop     }
944e45d8c71SBrad Bishop     if (scannedPowerOn)
945e45d8c71SBrad Bishop     {
946e45d8c71SBrad Bishop         return;
947e45d8c71SBrad Bishop     }
948e45d8c71SBrad Bishop 
949e45d8c71SBrad Bishop     if (!isPowerOn() && scannedPowerOff)
950e45d8c71SBrad Bishop     {
951e45d8c71SBrad Bishop         return;
952e45d8c71SBrad Bishop     }
953e45d8c71SBrad Bishop 
954e45d8c71SBrad Bishop     timer.expires_after(std::chrono::seconds(10));
955e45d8c71SBrad Bishop     timer.async_wait(
956e45d8c71SBrad Bishop         [&systemConfiguration](const boost::system::error_code& ec) {
957e45d8c71SBrad Bishop             if (ec == boost::asio::error::operation_aborted)
958e45d8c71SBrad Bishop             {
959e45d8c71SBrad Bishop                 return;
960e45d8c71SBrad Bishop             }
961e45d8c71SBrad Bishop 
962e45d8c71SBrad Bishop             bool powerOff = !isPowerOn();
963e45d8c71SBrad Bishop             for (const auto& [name, device] : lastJson.items())
964e45d8c71SBrad Bishop             {
965b7077437SPatrick Williams                 pruneDevice(systemConfiguration, powerOff, scannedPowerOff,
966b7077437SPatrick Williams                             name, device);
967e45d8c71SBrad Bishop             }
968e45d8c71SBrad Bishop 
969e45d8c71SBrad Bishop             scannedPowerOff = true;
970e45d8c71SBrad Bishop             if (!powerOff)
971e45d8c71SBrad Bishop             {
972e45d8c71SBrad Bishop                 scannedPowerOn = true;
973e45d8c71SBrad Bishop             }
974e45d8c71SBrad Bishop         });
975e45d8c71SBrad Bishop }
976e45d8c71SBrad Bishop 
977e45d8c71SBrad Bishop static std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
getDeviceInterfaces(const nlohmann::json & device)978e45d8c71SBrad Bishop     getDeviceInterfaces(const nlohmann::json& device)
979e45d8c71SBrad Bishop {
980e45d8c71SBrad Bishop     return inventory[device["Name"].get<std::string>()];
981e45d8c71SBrad Bishop }
982e45d8c71SBrad Bishop 
pruneConfiguration(nlohmann::json & systemConfiguration,sdbusplus::asio::object_server & objServer,bool powerOff,const std::string & name,const nlohmann::json & device)983e45d8c71SBrad Bishop static void pruneConfiguration(nlohmann::json& systemConfiguration,
984e45d8c71SBrad Bishop                                sdbusplus::asio::object_server& objServer,
985e45d8c71SBrad Bishop                                bool powerOff, const std::string& name,
986e45d8c71SBrad Bishop                                const nlohmann::json& device)
987e45d8c71SBrad Bishop {
988e45d8c71SBrad Bishop     if (powerOff && deviceRequiresPowerOn(device))
989e45d8c71SBrad Bishop     {
990e45d8c71SBrad Bishop         // power not on yet, don't know if it's there or not
991e45d8c71SBrad Bishop         return;
992e45d8c71SBrad Bishop     }
993e45d8c71SBrad Bishop 
994e45d8c71SBrad Bishop     auto& ifaces = getDeviceInterfaces(device);
995e45d8c71SBrad Bishop     for (auto& iface : ifaces)
996e45d8c71SBrad Bishop     {
997e45d8c71SBrad Bishop         auto sharedPtr = iface.lock();
998e45d8c71SBrad Bishop         if (!!sharedPtr)
999e45d8c71SBrad Bishop         {
1000e45d8c71SBrad Bishop             objServer.remove_interface(sharedPtr);
1001e45d8c71SBrad Bishop         }
1002e45d8c71SBrad Bishop     }
1003e45d8c71SBrad Bishop 
1004e45d8c71SBrad Bishop     ifaces.clear();
1005e45d8c71SBrad Bishop     systemConfiguration.erase(name);
10066eb60972SMatt Spinler     topology.remove(device["Name"].get<std::string>());
1007e45d8c71SBrad Bishop     logDeviceRemoved(device);
1008e45d8c71SBrad Bishop }
1009e45d8c71SBrad Bishop 
deriveNewConfiguration(const nlohmann::json & oldConfiguration,nlohmann::json & newConfiguration)1010e45d8c71SBrad Bishop static void deriveNewConfiguration(const nlohmann::json& oldConfiguration,
1011e45d8c71SBrad Bishop                                    nlohmann::json& newConfiguration)
1012e45d8c71SBrad Bishop {
1013e45d8c71SBrad Bishop     for (auto it = newConfiguration.begin(); it != newConfiguration.end();)
1014e45d8c71SBrad Bishop     {
1015e45d8c71SBrad Bishop         auto findKey = oldConfiguration.find(it.key());
1016e45d8c71SBrad Bishop         if (findKey != oldConfiguration.end())
1017e45d8c71SBrad Bishop         {
1018e45d8c71SBrad Bishop             it = newConfiguration.erase(it);
1019e45d8c71SBrad Bishop         }
1020e45d8c71SBrad Bishop         else
1021e45d8c71SBrad Bishop         {
1022e45d8c71SBrad Bishop             it++;
1023e45d8c71SBrad Bishop         }
1024e45d8c71SBrad Bishop     }
1025e45d8c71SBrad Bishop }
1026e45d8c71SBrad Bishop 
publishNewConfiguration(const size_t & instance,const size_t count,boost::asio::steady_timer & timer,nlohmann::json & systemConfiguration,const nlohmann::json newConfiguration,sdbusplus::asio::object_server & objServer)1027e45d8c71SBrad Bishop static void publishNewConfiguration(
1028e45d8c71SBrad Bishop     const size_t& instance, const size_t count,
1029e45d8c71SBrad Bishop     boost::asio::steady_timer& timer, nlohmann::json& systemConfiguration,
1030e45d8c71SBrad Bishop     // Gerrit discussion:
1031e45d8c71SBrad Bishop     // https://gerrit.openbmc-project.xyz/c/openbmc/entity-manager/+/52316/6
1032e45d8c71SBrad Bishop     //
1033e45d8c71SBrad Bishop     // Discord discussion:
1034e45d8c71SBrad Bishop     // https://discord.com/channels/775381525260664832/867820390406422538/958048437729910854
1035e45d8c71SBrad Bishop     //
1036e45d8c71SBrad Bishop     // NOLINTNEXTLINE(performance-unnecessary-value-param)
1037e45d8c71SBrad Bishop     const nlohmann::json newConfiguration,
1038e45d8c71SBrad Bishop     sdbusplus::asio::object_server& objServer)
1039e45d8c71SBrad Bishop {
1040e45d8c71SBrad Bishop     loadOverlays(newConfiguration);
1041e45d8c71SBrad Bishop 
104249a888c2SEd Tanous     boost::asio::post(io, [systemConfiguration]() {
1043e45d8c71SBrad Bishop         if (!writeJsonFiles(systemConfiguration))
1044e45d8c71SBrad Bishop         {
1045e45d8c71SBrad Bishop             std::cerr << "Error writing json files\n";
1046e45d8c71SBrad Bishop         }
1047e45d8c71SBrad Bishop     });
1048e45d8c71SBrad Bishop 
104949a888c2SEd Tanous     boost::asio::post(io, [&instance, count, &timer, newConfiguration,
105049a888c2SEd Tanous                            &systemConfiguration, &objServer]() {
1051e45d8c71SBrad Bishop         postToDbus(newConfiguration, systemConfiguration, objServer);
1052e45d8c71SBrad Bishop         if (count == instance)
1053e45d8c71SBrad Bishop         {
1054e45d8c71SBrad Bishop             startRemovedTimer(timer, systemConfiguration);
1055e45d8c71SBrad Bishop         }
1056e45d8c71SBrad Bishop     });
1057e45d8c71SBrad Bishop }
1058e45d8c71SBrad Bishop 
1059e45d8c71SBrad Bishop // main properties changed entry
propertiesChangedCallback(nlohmann::json & systemConfiguration,sdbusplus::asio::object_server & objServer)1060e45d8c71SBrad Bishop void propertiesChangedCallback(nlohmann::json& systemConfiguration,
1061e45d8c71SBrad Bishop                                sdbusplus::asio::object_server& objServer)
1062e45d8c71SBrad Bishop {
1063e45d8c71SBrad Bishop     static bool inProgress = false;
1064e45d8c71SBrad Bishop     static boost::asio::steady_timer timer(io);
1065e45d8c71SBrad Bishop     static size_t instance = 0;
1066e45d8c71SBrad Bishop     instance++;
1067e45d8c71SBrad Bishop     size_t count = instance;
1068e45d8c71SBrad Bishop 
1069bf263984SAnupama B R     timer.expires_after(std::chrono::milliseconds(500));
1070e45d8c71SBrad Bishop 
1071e45d8c71SBrad Bishop     // setup an async wait as we normally get flooded with new requests
1072e45d8c71SBrad Bishop     timer.async_wait([&systemConfiguration, &objServer,
1073e45d8c71SBrad Bishop                       count](const boost::system::error_code& ec) {
1074e45d8c71SBrad Bishop         if (ec == boost::asio::error::operation_aborted)
1075e45d8c71SBrad Bishop         {
1076e45d8c71SBrad Bishop             // we were cancelled
1077e45d8c71SBrad Bishop             return;
1078e45d8c71SBrad Bishop         }
1079e45d8c71SBrad Bishop         if (ec)
1080e45d8c71SBrad Bishop         {
1081e45d8c71SBrad Bishop             std::cerr << "async wait error " << ec << "\n";
1082e45d8c71SBrad Bishop             return;
1083e45d8c71SBrad Bishop         }
1084e45d8c71SBrad Bishop 
1085e45d8c71SBrad Bishop         if (inProgress)
1086e45d8c71SBrad Bishop         {
1087e45d8c71SBrad Bishop             propertiesChangedCallback(systemConfiguration, objServer);
1088e45d8c71SBrad Bishop             return;
1089e45d8c71SBrad Bishop         }
1090e45d8c71SBrad Bishop         inProgress = true;
1091e45d8c71SBrad Bishop 
1092e45d8c71SBrad Bishop         nlohmann::json oldConfiguration = systemConfiguration;
1093e45d8c71SBrad Bishop         auto missingConfigurations = std::make_shared<nlohmann::json>();
1094e45d8c71SBrad Bishop         *missingConfigurations = systemConfiguration;
1095e45d8c71SBrad Bishop 
1096e45d8c71SBrad Bishop         std::list<nlohmann::json> configurations;
1097e45d8c71SBrad Bishop         if (!loadConfigurations(configurations))
1098e45d8c71SBrad Bishop         {
1099e45d8c71SBrad Bishop             std::cerr << "Could not load configurations\n";
1100e45d8c71SBrad Bishop             inProgress = false;
1101e45d8c71SBrad Bishop             return;
1102e45d8c71SBrad Bishop         }
1103e45d8c71SBrad Bishop 
1104e45d8c71SBrad Bishop         auto perfScan = std::make_shared<PerformScan>(
1105e45d8c71SBrad Bishop             systemConfiguration, *missingConfigurations, configurations,
1106e45d8c71SBrad Bishop             objServer,
1107e45d8c71SBrad Bishop             [&systemConfiguration, &objServer, count, oldConfiguration,
1108e45d8c71SBrad Bishop              missingConfigurations]() {
1109e45d8c71SBrad Bishop                 // this is something that since ac has been applied to the bmc
1110e45d8c71SBrad Bishop                 // we saw, and we no longer see it
1111e45d8c71SBrad Bishop                 bool powerOff = !isPowerOn();
1112b7077437SPatrick Williams                 for (const auto& [name, device] :
1113b7077437SPatrick Williams                      missingConfigurations->items())
1114e45d8c71SBrad Bishop                 {
1115e45d8c71SBrad Bishop                     pruneConfiguration(systemConfiguration, objServer, powerOff,
1116e45d8c71SBrad Bishop                                        name, device);
1117e45d8c71SBrad Bishop                 }
1118e45d8c71SBrad Bishop 
1119e45d8c71SBrad Bishop                 nlohmann::json newConfiguration = systemConfiguration;
1120e45d8c71SBrad Bishop 
1121e45d8c71SBrad Bishop                 deriveNewConfiguration(oldConfiguration, newConfiguration);
1122e45d8c71SBrad Bishop 
1123e45d8c71SBrad Bishop                 for (const auto& [_, device] : newConfiguration.items())
1124e45d8c71SBrad Bishop                 {
1125e45d8c71SBrad Bishop                     logDeviceAdded(device);
1126e45d8c71SBrad Bishop                 }
1127e45d8c71SBrad Bishop 
1128e45d8c71SBrad Bishop                 inProgress = false;
1129e45d8c71SBrad Bishop 
113049a888c2SEd Tanous                 boost::asio::post(
1131b7077437SPatrick Williams                     io, std::bind_front(
1132b7077437SPatrick Williams                             publishNewConfiguration, std::ref(instance), count,
1133b7077437SPatrick Williams                             std::ref(timer), std::ref(systemConfiguration),
1134e45d8c71SBrad Bishop                             newConfiguration, std::ref(objServer)));
1135e45d8c71SBrad Bishop             });
1136e45d8c71SBrad Bishop         perfScan->run();
1137e45d8c71SBrad Bishop     });
1138e45d8c71SBrad Bishop }
1139e45d8c71SBrad Bishop 
11408d1ac3a2SMatt Spinler // Extract the D-Bus interfaces to probe from the JSON config files.
getProbeInterfaces()11418d1ac3a2SMatt Spinler static std::set<std::string> getProbeInterfaces()
11428d1ac3a2SMatt Spinler {
11438d1ac3a2SMatt Spinler     std::set<std::string> interfaces;
11448d1ac3a2SMatt Spinler     std::list<nlohmann::json> configurations;
11458d1ac3a2SMatt Spinler     if (!loadConfigurations(configurations))
11468d1ac3a2SMatt Spinler     {
11478d1ac3a2SMatt Spinler         return interfaces;
11488d1ac3a2SMatt Spinler     }
11498d1ac3a2SMatt Spinler 
11508d1ac3a2SMatt Spinler     for (auto it = configurations.begin(); it != configurations.end();)
11518d1ac3a2SMatt Spinler     {
11528d1ac3a2SMatt Spinler         auto findProbe = it->find("Probe");
11538d1ac3a2SMatt Spinler         if (findProbe == it->end())
11548d1ac3a2SMatt Spinler         {
11558d1ac3a2SMatt Spinler             std::cerr << "configuration file missing probe:\n " << *it << "\n";
11568d1ac3a2SMatt Spinler             it++;
11578d1ac3a2SMatt Spinler             continue;
11588d1ac3a2SMatt Spinler         }
11598d1ac3a2SMatt Spinler 
11608d1ac3a2SMatt Spinler         nlohmann::json probeCommand;
11618d1ac3a2SMatt Spinler         if ((*findProbe).type() != nlohmann::json::value_t::array)
11628d1ac3a2SMatt Spinler         {
11638d1ac3a2SMatt Spinler             probeCommand = nlohmann::json::array();
11648d1ac3a2SMatt Spinler             probeCommand.push_back(*findProbe);
11658d1ac3a2SMatt Spinler         }
11668d1ac3a2SMatt Spinler         else
11678d1ac3a2SMatt Spinler         {
11688d1ac3a2SMatt Spinler             probeCommand = *findProbe;
11698d1ac3a2SMatt Spinler         }
11708d1ac3a2SMatt Spinler 
11718d1ac3a2SMatt Spinler         for (const nlohmann::json& probeJson : probeCommand)
11728d1ac3a2SMatt Spinler         {
11738d1ac3a2SMatt Spinler             const std::string* probe = probeJson.get_ptr<const std::string*>();
11748d1ac3a2SMatt Spinler             if (probe == nullptr)
11758d1ac3a2SMatt Spinler             {
11768d1ac3a2SMatt Spinler                 std::cerr << "Probe statement wasn't a string, can't parse";
11778d1ac3a2SMatt Spinler                 continue;
11788d1ac3a2SMatt Spinler             }
11798d1ac3a2SMatt Spinler             // Skip it if the probe cmd doesn't contain an interface.
11808d1ac3a2SMatt Spinler             if (findProbeType(*probe))
11818d1ac3a2SMatt Spinler             {
11828d1ac3a2SMatt Spinler                 continue;
11838d1ac3a2SMatt Spinler             }
11848d1ac3a2SMatt Spinler 
11858d1ac3a2SMatt Spinler             // syntax requires probe before first open brace
11868d1ac3a2SMatt Spinler             auto findStart = probe->find('(');
11878d1ac3a2SMatt Spinler             if (findStart != std::string::npos)
11888d1ac3a2SMatt Spinler             {
11898d1ac3a2SMatt Spinler                 std::string interface = probe->substr(0, findStart);
11908d1ac3a2SMatt Spinler                 interfaces.emplace(interface);
11918d1ac3a2SMatt Spinler             }
11928d1ac3a2SMatt Spinler         }
11938d1ac3a2SMatt Spinler         it++;
11948d1ac3a2SMatt Spinler     }
11958d1ac3a2SMatt Spinler 
11968d1ac3a2SMatt Spinler     return interfaces;
11978d1ac3a2SMatt Spinler }
11988d1ac3a2SMatt Spinler 
11998d1ac3a2SMatt Spinler // Check if InterfacesAdded payload contains an iface that needs probing.
iaContainsProbeInterface(sdbusplus::message_t & msg,const std::set<std::string> & probeInterfaces)1200b7077437SPatrick Williams static bool iaContainsProbeInterface(
1201b7077437SPatrick Williams     sdbusplus::message_t& msg, const std::set<std::string>& probeInterfaces)
12028d1ac3a2SMatt Spinler {
12038d1ac3a2SMatt Spinler     sdbusplus::message::object_path path;
12048d1ac3a2SMatt Spinler     DBusObject interfaces;
12058d1ac3a2SMatt Spinler     std::set<std::string> interfaceSet;
12068d1ac3a2SMatt Spinler     std::set<std::string> intersect;
12078d1ac3a2SMatt Spinler 
12088d1ac3a2SMatt Spinler     msg.read(path, interfaces);
12098d1ac3a2SMatt Spinler 
12108d1ac3a2SMatt Spinler     std::for_each(interfaces.begin(), interfaces.end(),
12118d1ac3a2SMatt Spinler                   [&interfaceSet](const auto& iface) {
12128d1ac3a2SMatt Spinler                       interfaceSet.insert(iface.first);
12138d1ac3a2SMatt Spinler                   });
12148d1ac3a2SMatt Spinler 
12158d1ac3a2SMatt Spinler     std::set_intersection(interfaceSet.begin(), interfaceSet.end(),
12168d1ac3a2SMatt Spinler                           probeInterfaces.begin(), probeInterfaces.end(),
12178d1ac3a2SMatt Spinler                           std::inserter(intersect, intersect.end()));
12188d1ac3a2SMatt Spinler     return !intersect.empty();
12198d1ac3a2SMatt Spinler }
12208d1ac3a2SMatt Spinler 
12218d1ac3a2SMatt Spinler // Check if InterfacesRemoved payload contains an iface that needs probing.
irContainsProbeInterface(sdbusplus::message_t & msg,const std::set<std::string> & probeInterfaces)1222b7077437SPatrick Williams static bool irContainsProbeInterface(
1223b7077437SPatrick Williams     sdbusplus::message_t& msg, const std::set<std::string>& probeInterfaces)
12248d1ac3a2SMatt Spinler {
12258d1ac3a2SMatt Spinler     sdbusplus::message::object_path path;
12268d1ac3a2SMatt Spinler     std::set<std::string> interfaces;
12278d1ac3a2SMatt Spinler     std::set<std::string> intersect;
12288d1ac3a2SMatt Spinler 
12298d1ac3a2SMatt Spinler     msg.read(path, interfaces);
12308d1ac3a2SMatt Spinler 
12318d1ac3a2SMatt Spinler     std::set_intersection(interfaces.begin(), interfaces.end(),
12328d1ac3a2SMatt Spinler                           probeInterfaces.begin(), probeInterfaces.end(),
12338d1ac3a2SMatt Spinler                           std::inserter(intersect, intersect.end()));
12348d1ac3a2SMatt Spinler     return !intersect.empty();
12358d1ac3a2SMatt Spinler }
12368d1ac3a2SMatt Spinler 
main()1237e45d8c71SBrad Bishop int main()
1238e45d8c71SBrad Bishop {
1239e45d8c71SBrad Bishop     // setup connection to dbus
1240e45d8c71SBrad Bishop     systemBus = std::make_shared<sdbusplus::asio::connection>(io);
1241e45d8c71SBrad Bishop     systemBus->request_name("xyz.openbmc_project.EntityManager");
1242e45d8c71SBrad Bishop 
1243a331567eSNan Zhou     // The EntityManager object itself doesn't expose any properties.
1244a331567eSNan Zhou     // No need to set up ObjectManager for the |EntityManager| object.
1245a331567eSNan Zhou     sdbusplus::asio::object_server objServer(systemBus, /*skipManager=*/true);
1246a331567eSNan Zhou 
1247a331567eSNan Zhou     // All other objects that EntityManager currently support are under the
1248a331567eSNan Zhou     // inventory subtree.
1249a331567eSNan Zhou     // See the discussion at
1250a331567eSNan Zhou     // https://discord.com/channels/775381525260664832/1018929092009144380
1251a331567eSNan Zhou     objServer.add_manager("/xyz/openbmc_project/inventory");
1252e45d8c71SBrad Bishop 
1253e45d8c71SBrad Bishop     std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1254e45d8c71SBrad Bishop         objServer.add_interface("/xyz/openbmc_project/EntityManager",
1255e45d8c71SBrad Bishop                                 "xyz.openbmc_project.EntityManager");
1256e45d8c71SBrad Bishop 
1257e45d8c71SBrad Bishop     // to keep reference to the match / filter objects so they don't get
1258e45d8c71SBrad Bishop     // destroyed
1259e45d8c71SBrad Bishop 
1260e45d8c71SBrad Bishop     nlohmann::json systemConfiguration = nlohmann::json::object();
1261e45d8c71SBrad Bishop 
12628d1ac3a2SMatt Spinler     std::set<std::string> probeInterfaces = getProbeInterfaces();
12638d1ac3a2SMatt Spinler 
1264e45d8c71SBrad Bishop     // We need a poke from DBus for static providers that create all their
1265e45d8c71SBrad Bishop     // objects prior to claiming a well-known name, and thus don't emit any
1266e45d8c71SBrad Bishop     // org.freedesktop.DBus.Properties signals.  Similarly if a process exits
1267e45d8c71SBrad Bishop     // for any reason, expected or otherwise, we'll need a poke to remove
1268e45d8c71SBrad Bishop     // entities from DBus.
12692af39224SPatrick Williams     sdbusplus::bus::match_t nameOwnerChangedMatch(
12702af39224SPatrick Williams         static_cast<sdbusplus::bus_t&>(*systemBus),
1271e45d8c71SBrad Bishop         sdbusplus::bus::match::rules::nameOwnerChanged(),
12727b8786f4SPatrick Williams         [&](sdbusplus::message_t& m) {
1273df190619SPatrick Williams             auto [name, oldOwner,
1274df190619SPatrick Williams                   newOwner] = m.unpack<std::string, std::string, std::string>();
12757b8786f4SPatrick Williams 
12767b8786f4SPatrick Williams             if (name.starts_with(':'))
12777b8786f4SPatrick Williams             {
12787b8786f4SPatrick Williams                 // We should do nothing with unique-name connections.
12797b8786f4SPatrick Williams                 return;
12807b8786f4SPatrick Williams             }
12817b8786f4SPatrick Williams 
1282e45d8c71SBrad Bishop             propertiesChangedCallback(systemConfiguration, objServer);
1283e45d8c71SBrad Bishop         });
1284e45d8c71SBrad Bishop     // We also need a poke from DBus when new interfaces are created or
1285e45d8c71SBrad Bishop     // destroyed.
12862af39224SPatrick Williams     sdbusplus::bus::match_t interfacesAddedMatch(
12872af39224SPatrick Williams         static_cast<sdbusplus::bus_t&>(*systemBus),
1288e45d8c71SBrad Bishop         sdbusplus::bus::match::rules::interfacesAdded(),
12898d1ac3a2SMatt Spinler         [&](sdbusplus::message_t& msg) {
12908d1ac3a2SMatt Spinler             if (iaContainsProbeInterface(msg, probeInterfaces))
12918d1ac3a2SMatt Spinler             {
1292e45d8c71SBrad Bishop                 propertiesChangedCallback(systemConfiguration, objServer);
12938d1ac3a2SMatt Spinler             }
1294e45d8c71SBrad Bishop         });
12952af39224SPatrick Williams     sdbusplus::bus::match_t interfacesRemovedMatch(
12962af39224SPatrick Williams         static_cast<sdbusplus::bus_t&>(*systemBus),
1297e45d8c71SBrad Bishop         sdbusplus::bus::match::rules::interfacesRemoved(),
12988d1ac3a2SMatt Spinler         [&](sdbusplus::message_t& msg) {
12998d1ac3a2SMatt Spinler             if (irContainsProbeInterface(msg, probeInterfaces))
13008d1ac3a2SMatt Spinler             {
1301e45d8c71SBrad Bishop                 propertiesChangedCallback(systemConfiguration, objServer);
13028d1ac3a2SMatt Spinler             }
1303e45d8c71SBrad Bishop         });
1304e45d8c71SBrad Bishop 
130549a888c2SEd Tanous     boost::asio::post(io, [&]() {
130649a888c2SEd Tanous         propertiesChangedCallback(systemConfiguration, objServer);
130749a888c2SEd Tanous     });
1308e45d8c71SBrad Bishop 
1309e45d8c71SBrad Bishop     entityIface->register_method("ReScan", [&]() {
1310e45d8c71SBrad Bishop         propertiesChangedCallback(systemConfiguration, objServer);
1311e45d8c71SBrad Bishop     });
1312d97c6319SJohn Edward Broadbent     tryIfaceInitialize(entityIface);
1313e45d8c71SBrad Bishop 
1314e45d8c71SBrad Bishop     if (fwVersionIsSame())
1315e45d8c71SBrad Bishop     {
1316e45d8c71SBrad Bishop         if (std::filesystem::is_regular_file(currentConfiguration))
1317e45d8c71SBrad Bishop         {
1318e45d8c71SBrad Bishop             // this file could just be deleted, but it's nice for debug
1319e45d8c71SBrad Bishop             std::filesystem::create_directory(tempConfigDir);
1320e45d8c71SBrad Bishop             std::filesystem::remove(lastConfiguration);
1321e45d8c71SBrad Bishop             std::filesystem::copy(currentConfiguration, lastConfiguration);
1322e45d8c71SBrad Bishop             std::filesystem::remove(currentConfiguration);
1323e45d8c71SBrad Bishop 
1324e45d8c71SBrad Bishop             std::ifstream jsonStream(lastConfiguration);
1325e45d8c71SBrad Bishop             if (jsonStream.good())
1326e45d8c71SBrad Bishop             {
1327e45d8c71SBrad Bishop                 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1328e45d8c71SBrad Bishop                 if (data.is_discarded())
1329e45d8c71SBrad Bishop                 {
1330b7077437SPatrick Williams                     std::cerr
1331b7077437SPatrick Williams                         << "syntax error in " << lastConfiguration << "\n";
1332e45d8c71SBrad Bishop                 }
1333e45d8c71SBrad Bishop                 else
1334e45d8c71SBrad Bishop                 {
1335e45d8c71SBrad Bishop                     lastJson = std::move(data);
1336e45d8c71SBrad Bishop                 }
1337e45d8c71SBrad Bishop             }
1338e45d8c71SBrad Bishop             else
1339e45d8c71SBrad Bishop             {
1340e45d8c71SBrad Bishop                 std::cerr << "unable to open " << lastConfiguration << "\n";
1341e45d8c71SBrad Bishop             }
1342e45d8c71SBrad Bishop         }
1343e45d8c71SBrad Bishop     }
1344e45d8c71SBrad Bishop     else
1345e45d8c71SBrad Bishop     {
1346e45d8c71SBrad Bishop         // not an error, just logging at this level to make it in the journal
1347e45d8c71SBrad Bishop         std::cerr << "Clearing previous configuration\n";
1348e45d8c71SBrad Bishop         std::filesystem::remove(currentConfiguration);
1349e45d8c71SBrad Bishop     }
1350e45d8c71SBrad Bishop 
1351e45d8c71SBrad Bishop     // some boards only show up after power is on, we want to not say they are
1352e45d8c71SBrad Bishop     // removed until the same state happens
1353e45d8c71SBrad Bishop     setupPowerMatch(systemBus);
1354e45d8c71SBrad Bishop 
1355e45d8c71SBrad Bishop     io.run();
1356e45d8c71SBrad Bishop 
1357e45d8c71SBrad Bishop     return 0;
1358e45d8c71SBrad Bishop }
1359