blob: 8bd97fd4d71ea6f00fe64dbeb7de95974e8618a6 [file] [log] [blame]
James Feist3cb5fec2018-01-23 14:41:51 -08001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
Brad Bishop1fb9f3f2020-08-28 08:15:13 -040016/// \file EntityManager.cpp
James Feist3cb5fec2018-01-23 14:41:51 -080017
James Feist1df06a42019-04-11 14:23:04 -070018#include "EntityManager.hpp"
19
Patrick Venturea49dc332019-10-26 08:32:02 -070020#include "Overlay.hpp"
21#include "Utils.hpp"
James Feist481c5d52019-08-13 14:40:40 -070022#include "VariantVisitors.hpp"
23
James Feist11be6672018-04-06 14:05:32 -070024#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070025#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080026#include <boost/algorithm/string/predicate.hpp>
27#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070028#include <boost/algorithm/string/split.hpp>
James Feist02d2b932020-02-06 16:28:48 -080029#include <boost/asio/io_context.hpp>
James Feistb1728ca2020-04-30 15:40:55 -070030#include <boost/asio/steady_timer.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080031#include <boost/container/flat_map.hpp>
32#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070033#include <boost/range/iterator_range.hpp>
James Feist8c505da2020-05-28 10:06:33 -070034#include <nlohmann/json.hpp>
35#include <sdbusplus/asio/connection.hpp>
36#include <sdbusplus/asio/object_server.hpp>
37
Igor Kononenko9fd87e52020-10-06 01:18:17 +030038#include <charconv>
James Feist637b3ef2019-04-15 16:35:30 -070039#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080040#include <fstream>
41#include <iostream>
James Feista465ccc2019-02-08 12:51:01 -080042#include <regex>
James Feista465ccc2019-02-08 12:51:01 -080043#include <variant>
James Feista465ccc2019-02-08 12:51:01 -080044constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
45constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070046constexpr const char* tempConfigDir = "/tmp/configuration/";
47constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
48constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080049constexpr const char* globalSchema = "global.json";
Ed Tanous07d467b2021-02-23 14:48:37 -080050constexpr const bool debug = false;
James Feistf1b14142019-04-10 15:22:09 -070051
Andrew Jeffery47af65a2021-12-01 14:16:31 +103052static const boost::container::flat_map<const char*, probe_type_codes, CmpStr>
Ed Tanous07d467b2021-02-23 14:48:37 -080053 probeTypes{{{"FALSE", probe_type_codes::FALSE_T},
54 {"TRUE", probe_type_codes::TRUE_T},
55 {"AND", probe_type_codes::AND},
56 {"OR", probe_type_codes::OR},
57 {"FOUND", probe_type_codes::FOUND},
58 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080059
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020060static constexpr std::array<const char*, 6> settableInterfaces = {
61 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist68500ff2018-08-08 15:40:42 -070062using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080063 std::variant<std::vector<std::string>, std::vector<double>, std::string,
64 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
65 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080066
67using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070068 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080069 boost::container::flat_map<
70 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070071 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080072
James Feistd58879a82019-09-11 11:26:07 -070073// store reference to all interfaces so we can destroy them later
74boost::container::flat_map<
James Feist02d2b932020-02-06 16:28:48 -080075 std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
James Feistd58879a82019-09-11 11:26:07 -070076 inventory;
77
James Feist3cb5fec2018-01-23 14:41:51 -080078// todo: pass this through nicer
Ed Tanous07d467b2021-02-23 14:48:37 -080079std::shared_ptr<sdbusplus::asio::connection> systemBus;
Andrew Jeffery47af65a2021-12-01 14:16:31 +103080nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -080081
James Feist02d2b932020-02-06 16:28:48 -080082boost::asio::io_context io;
83
Ed Tanous07d467b2021-02-23 14:48:37 -080084const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
85const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -080086
James Feistd58879a82019-09-11 11:26:07 -070087static std::shared_ptr<sdbusplus::asio::dbus_interface>
88 createInterface(sdbusplus::asio::object_server& objServer,
89 const std::string& path, const std::string& interface,
James Feist02d2b932020-02-06 16:28:48 -080090 const std::string& parent, bool checkNull = false)
James Feistd58879a82019-09-11 11:26:07 -070091{
James Feist02d2b932020-02-06 16:28:48 -080092 // on first add we have no reason to check for null before add, as there
93 // won't be any. For dynamically added interfaces, we check for null so that
94 // a constant delete/add will not create a memory leak
95
96 auto ptr = objServer.add_interface(path, interface);
97 auto& dataVector = inventory[parent];
98 if (checkNull)
99 {
100 auto it = std::find_if(dataVector.begin(), dataVector.end(),
101 [](const auto& p) { return p.expired(); });
102 if (it != dataVector.end())
103 {
104 *it = ptr;
105 return ptr;
106 }
107 }
108 dataVector.emplace_back(ptr);
109 return ptr;
James Feistd58879a82019-09-11 11:26:07 -0700110}
111
James Feist8f2710a2018-05-09 17:18:55 -0700112// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800113bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800114{
James Feist1df06a42019-04-11 14:23:04 -0700115 std::filesystem::create_directory(configurationOutDir);
116 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700117 if (!output.good())
118 {
119 return false;
120 }
James Feist1b2e2242018-01-30 13:45:19 -0800121 output << systemConfiguration.dump(4);
122 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700123 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700124}
James Feist1b2e2242018-01-30 13:45:19 -0800125
James Feist97a63f12018-05-17 13:50:57 -0700126template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800127bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
128 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700129{
130 try
131 {
132 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800133 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700134 ref = value;
135 return true;
136 }
James Feist98132792019-07-09 13:29:09 -0700137 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700138 {
139 return false;
140 }
141}
James Feistbb43d022018-06-12 15:44:33 -0700142
James Feistebcc26b2019-03-22 12:30:43 -0700143// template function to add array as dbus property
144template <typename PropertyType>
145void addArrayToDbus(const std::string& name, const nlohmann::json& array,
146 sdbusplus::asio::dbus_interface* iface,
147 sdbusplus::asio::PropertyPermission permission,
148 nlohmann::json& systemConfiguration,
149 const std::string& jsonPointerString)
150{
151 std::vector<PropertyType> values;
152 for (const auto& property : array)
153 {
154 auto ptr = property.get_ptr<const PropertyType*>();
155 if (ptr != nullptr)
156 {
157 values.emplace_back(*ptr);
158 }
159 }
160
161 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
162 {
163 iface->register_property(name, values);
164 }
165 else
166 {
167 iface->register_property(
168 name, values,
169 [&systemConfiguration,
170 jsonPointerString{std::string(jsonPointerString)}](
171 const std::vector<PropertyType>& newVal,
172 std::vector<PropertyType>& val) {
173 val = newVal;
174 if (!setJsonFromPointer(jsonPointerString, val,
175 systemConfiguration))
176 {
177 std::cerr << "error setting json field\n";
178 return -1;
179 }
180 if (!writeJsonFiles(systemConfiguration))
181 {
182 std::cerr << "error setting json file\n";
183 return -1;
184 }
185 return 1;
186 });
187 }
188}
189
James Feistbb43d022018-06-12 15:44:33 -0700190template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800191void addProperty(const std::string& propertyName, const PropertyType& value,
192 sdbusplus::asio::dbus_interface* iface,
193 nlohmann::json& systemConfiguration,
194 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700195 sdbusplus::asio::PropertyPermission permission)
196{
197 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
198 {
199 iface->register_property(propertyName, value);
200 return;
201 }
James Feist68500ff2018-08-08 15:40:42 -0700202 iface->register_property(
203 propertyName, value,
204 [&systemConfiguration,
205 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800206 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700207 val = newVal;
208 if (!setJsonFromPointer(jsonPointerString, val,
209 systemConfiguration))
210 {
211 std::cerr << "error setting json field\n";
212 return -1;
213 }
James Feistc6248a52018-08-14 10:09:45 -0700214 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700215 {
216 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700217 return -1;
218 }
219 return 1;
220 });
221}
222
223void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800224 const std::string& jsonPointerPath,
225 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
226 sdbusplus::asio::object_server& objServer,
227 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700228{
229 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
230 iface->register_method(
231 "Delete", [&objServer, &systemConfiguration, interface,
232 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700233 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700234 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700235 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700236 {
237 // this technically can't happen as the pointer is pointing to
238 // us
239 throw DBusInternalError();
240 }
241 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feistc6248a52018-08-14 10:09:45 -0700242 systemConfiguration[ptr] = nullptr;
243
James Feist02d2b932020-02-06 16:28:48 -0800244 // todo(james): dig through sdbusplus to find out why we can't
245 // delete it in a method call
246 io.post([&objServer, dbusInterface]() mutable {
247 objServer.remove_interface(dbusInterface);
248 });
249
James Feistc6248a52018-08-14 10:09:45 -0700250 if (!writeJsonFiles(systemConfiguration))
251 {
252 std::cerr << "error setting json file\n";
253 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700254 }
James Feist68500ff2018-08-08 15:40:42 -0700255 });
James Feistbb43d022018-06-12 15:44:33 -0700256}
257
James Feist1b2e2242018-01-30 13:45:19 -0800258// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700259void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800260 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
261 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
262 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700263 sdbusplus::asio::PropertyPermission permission =
264 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800265{
James Feista465ccc2019-02-08 12:51:01 -0800266 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800267 {
James Feist8f2710a2018-05-09 17:18:55 -0700268 auto type = dictPair.value().type();
269 bool array = false;
270 if (dictPair.value().type() == nlohmann::json::value_t::array)
271 {
272 array = true;
273 if (!dictPair.value().size())
274 {
275 continue;
276 }
277 type = dictPair.value()[0].type();
278 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800279 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700280 {
281 if (arrayItem.type() != type)
282 {
283 isLegal = false;
284 break;
285 }
286 }
287 if (!isLegal)
288 {
289 std::cerr << "dbus format error" << dictPair.value() << "\n";
290 continue;
291 }
James Feista218ddb2019-04-11 14:01:31 -0700292 }
293 if (type == nlohmann::json::value_t::object)
294 {
295 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700296 }
James Feist97a63f12018-05-17 13:50:57 -0700297 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700298 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
299 {
300 // all setable numbers are doubles as it is difficult to always
301 // create a configuration file with all whole numbers as decimals
302 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700303 if (array)
304 {
305 if (dictPair.value()[0].is_number())
306 {
307 type = nlohmann::json::value_t::number_float;
308 }
309 }
310 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700311 {
312 type = nlohmann::json::value_t::number_float;
313 }
314 }
315
James Feist8f2710a2018-05-09 17:18:55 -0700316 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800317 {
James Feist9eb0b582018-04-27 12:15:46 -0700318 case (nlohmann::json::value_t::boolean):
319 {
James Feist8f2710a2018-05-09 17:18:55 -0700320 if (array)
321 {
322 // todo: array of bool isn't detected correctly by
323 // sdbusplus, change it to numbers
324 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700325 iface.get(), permission,
326 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700327 }
James Feistbb43d022018-06-12 15:44:33 -0700328
James Feist97a63f12018-05-17 13:50:57 -0700329 else
330 {
James Feistbb43d022018-06-12 15:44:33 -0700331 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700332 iface.get(), systemConfiguration, key,
333 permission);
James Feist97a63f12018-05-17 13:50:57 -0700334 }
James Feist9eb0b582018-04-27 12:15:46 -0700335 break;
336 }
337 case (nlohmann::json::value_t::number_integer):
338 {
James Feist8f2710a2018-05-09 17:18:55 -0700339 if (array)
340 {
341 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700342 iface.get(), permission,
343 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700344 }
345 else
346 {
James Feistbb43d022018-06-12 15:44:33 -0700347 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700348 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700349 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700350 }
James Feist9eb0b582018-04-27 12:15:46 -0700351 break;
352 }
353 case (nlohmann::json::value_t::number_unsigned):
354 {
James Feist8f2710a2018-05-09 17:18:55 -0700355 if (array)
356 {
357 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700358 iface.get(), permission,
359 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700360 }
361 else
362 {
James Feistbb43d022018-06-12 15:44:33 -0700363 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700364 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700365 systemConfiguration, key,
366 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700367 }
James Feist9eb0b582018-04-27 12:15:46 -0700368 break;
369 }
370 case (nlohmann::json::value_t::number_float):
371 {
James Feist8f2710a2018-05-09 17:18:55 -0700372 if (array)
373 {
374 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700375 iface.get(), permission,
376 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700377 }
James Feistbb43d022018-06-12 15:44:33 -0700378
James Feist97a63f12018-05-17 13:50:57 -0700379 else
380 {
James Feistbb43d022018-06-12 15:44:33 -0700381 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700382 iface.get(), systemConfiguration, key,
383 permission);
James Feist97a63f12018-05-17 13:50:57 -0700384 }
James Feist9eb0b582018-04-27 12:15:46 -0700385 break;
386 }
387 case (nlohmann::json::value_t::string):
388 {
James Feist8f2710a2018-05-09 17:18:55 -0700389 if (array)
390 {
James Feistebcc26b2019-03-22 12:30:43 -0700391 addArrayToDbus<std::string>(
392 dictPair.key(), dictPair.value(), iface.get(),
393 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700394 }
395 else
396 {
James Feistc6248a52018-08-14 10:09:45 -0700397 addProperty(
398 dictPair.key(), dictPair.value().get<std::string>(),
399 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700400 }
James Feist9eb0b582018-04-27 12:15:46 -0700401 break;
402 }
James Feist0eb40352019-04-09 14:44:04 -0700403 default:
404 {
James Feista218ddb2019-04-11 14:01:31 -0700405 std::cerr << "Unexpected json type in system configuration "
406 << dictPair.key() << ": "
407 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700408 break;
409 }
James Feist1b2e2242018-01-30 13:45:19 -0800410 }
411 }
James Feistc6248a52018-08-14 10:09:45 -0700412 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
413 {
414 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
415 systemConfiguration);
416 }
James Feist8f2710a2018-05-09 17:18:55 -0700417 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800418}
419
James Feista465ccc2019-02-08 12:51:01 -0800420sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700421{
422 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
423 interface) != settableInterfaces.end()
424 ? sdbusplus::asio::PropertyPermission::readWrite
425 : sdbusplus::asio::PropertyPermission::readOnly;
426}
427
James Feista465ccc2019-02-08 12:51:01 -0800428void createAddObjectMethod(const std::string& jsonPointerPath,
429 const std::string& path,
430 nlohmann::json& systemConfiguration,
James Feistd58879a82019-09-11 11:26:07 -0700431 sdbusplus::asio::object_server& objServer,
432 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700433{
James Feistd58879a82019-09-11 11:26:07 -0700434 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
435 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700436
437 iface->register_method(
438 "AddObject",
439 [&systemConfiguration, &objServer,
James Feistd58879a82019-09-11 11:26:07 -0700440 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
441 board](const boost::container::flat_map<std::string, JsonVariantType>&
442 data) {
James Feist68500ff2018-08-08 15:40:42 -0700443 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800444 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700445 auto findExposes = base.find("Exposes");
446
447 if (findExposes == base.end())
448 {
449 throw std::invalid_argument("Entity must have children.");
450 }
451
452 // this will throw invalid-argument to sdbusplus if invalid json
453 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800454 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700455 {
James Feista465ccc2019-02-08 12:51:01 -0800456 nlohmann::json& newJson = newData[item.first];
457 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
458 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700459 }
460
461 auto findName = newData.find("Name");
462 auto findType = newData.find("Type");
463 if (findName == newData.end() || findType == newData.end())
464 {
465 throw std::invalid_argument("AddObject missing Name or Type");
466 }
James Feista465ccc2019-02-08 12:51:01 -0800467 const std::string* type = findType->get_ptr<const std::string*>();
468 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700469 if (type == nullptr || name == nullptr)
470 {
471 throw std::invalid_argument("Type and Name must be a string.");
472 }
473
James Feist02d2b932020-02-06 16:28:48 -0800474 bool foundNull = false;
James Feist68500ff2018-08-08 15:40:42 -0700475 size_t lastIndex = 0;
476 // we add in the "exposes"
James Feist02d2b932020-02-06 16:28:48 -0800477 for (const auto& expose : *findExposes)
James Feist68500ff2018-08-08 15:40:42 -0700478 {
James Feist02d2b932020-02-06 16:28:48 -0800479 if (expose.is_null())
480 {
481 foundNull = true;
482 continue;
483 }
484
485 if (expose["Name"] == *name && expose["Type"] == *type)
James Feist68500ff2018-08-08 15:40:42 -0700486 {
487 throw std::invalid_argument(
488 "Field already in JSON, not adding");
489 }
James Feist02d2b932020-02-06 16:28:48 -0800490
491 if (foundNull)
492 {
493 continue;
494 }
495
James Feist68500ff2018-08-08 15:40:42 -0700496 lastIndex++;
497 }
498
499 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
500 *type + ".json");
501 // todo(james) we might want to also make a list of 'can add'
502 // interfaces but for now I think the assumption if there is a
503 // schema avaliable that it is allowed to update is fine
504 if (!schemaFile.good())
505 {
506 throw std::invalid_argument(
507 "No schema avaliable, cannot validate.");
508 }
509 nlohmann::json schema =
510 nlohmann::json::parse(schemaFile, nullptr, false);
511 if (schema.is_discarded())
512 {
513 std::cerr << "Schema not legal" << *type << ".json\n";
514 throw DBusInternalError();
515 }
516 if (!validateJson(schema, newData))
517 {
518 throw std::invalid_argument("Data does not match schema");
519 }
James Feist02d2b932020-02-06 16:28:48 -0800520 if (foundNull)
521 {
522 findExposes->at(lastIndex) = newData;
523 }
524 else
525 {
526 findExposes->push_back(newData);
527 }
James Feist68500ff2018-08-08 15:40:42 -0700528 if (!writeJsonFiles(systemConfiguration))
529 {
530 std::cerr << "Error writing json files\n";
531 throw DBusInternalError();
532 }
533 std::string dbusName = *name;
534
535 std::regex_replace(dbusName.begin(), dbusName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800536 dbusName.end(), illegalDbusMemberRegex, "_");
James Feistd58879a82019-09-11 11:26:07 -0700537
538 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
539 createInterface(objServer, path + "/" + dbusName,
540 "xyz.openbmc_project.Configuration." + *type,
James Feist02d2b932020-02-06 16:28:48 -0800541 board, true);
James Feist68500ff2018-08-08 15:40:42 -0700542 // permission is read-write, as since we just created it, must be
543 // runtime modifiable
544 populateInterfaceFromJson(
545 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700546 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700547 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700548 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700549 });
550 iface->initialize();
551}
552
James Feista465ccc2019-02-08 12:51:01 -0800553void postToDbus(const nlohmann::json& newConfiguration,
554 nlohmann::json& systemConfiguration,
555 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800556
James Feist1b2e2242018-01-30 13:45:19 -0800557{
James Feist97a63f12018-05-17 13:50:57 -0700558 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800559 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800560 {
James Feistf1b14142019-04-10 15:22:09 -0700561 std::string boardKey = boardPair.value()["Name"];
James Feistd58879a82019-09-11 11:26:07 -0700562 std::string boardKeyOrig = boardPair.value()["Name"];
James Feistf1b14142019-04-10 15:22:09 -0700563 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700564 // loop through newConfiguration, but use values from system
565 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700566 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700567 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800568 std::string boardType;
569 if (findBoardType != boardValues.end() &&
570 findBoardType->type() == nlohmann::json::value_t::string)
571 {
572 boardType = findBoardType->get<std::string>();
573 std::regex_replace(boardType.begin(), boardType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800574 boardType.end(), illegalDbusMemberRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800575 }
576 else
577 {
578 std::cerr << "Unable to find type for " << boardKey
579 << " reverting to Chassis.\n";
580 boardType = "Chassis";
581 }
James Feist11be6672018-04-06 14:05:32 -0700582 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800583
584 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800585 illegalDbusMemberRegex, "_");
586 std::string boardName = "/xyz/openbmc_project/inventory/system/";
587 boardName += boardtypeLower;
588 boardName += "/";
589 boardName += boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800590
James Feistd58879a82019-09-11 11:26:07 -0700591 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
592 createInterface(objServer, boardName,
593 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700594
James Feistd58879a82019-09-11 11:26:07 -0700595 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
596 createInterface(objServer, boardName,
597 "xyz.openbmc_project.Inventory.Item." + boardType,
598 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -0700599
James Feist68500ff2018-08-08 15:40:42 -0700600 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a82019-09-11 11:26:07 -0700601 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -0700602
James Feist97a63f12018-05-17 13:50:57 -0700603 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700604 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700605 jsonPointerPath += "/";
606 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800607 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700608 {
609 if (boardField.value().type() == nlohmann::json::value_t::object)
610 {
James Feistd58879a82019-09-11 11:26:07 -0700611 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
612 createInterface(objServer, boardName, boardField.key(),
613 boardKeyOrig);
614
James Feistc6248a52018-08-14 10:09:45 -0700615 populateInterfaceFromJson(systemConfiguration,
616 jsonPointerPath + boardField.key(),
617 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700618 }
619 }
James Feist97a63f12018-05-17 13:50:57 -0700620
James Feist1e3e6982018-08-03 16:09:28 -0700621 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800622 if (exposes == boardValues.end())
623 {
624 continue;
625 }
James Feist97a63f12018-05-17 13:50:57 -0700626 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700627 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700628
629 // store the board level pointer so we can modify it on the way down
630 std::string jsonPointerPathBoard = jsonPointerPath;
631 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -0800632 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -0800633 {
James Feist97a63f12018-05-17 13:50:57 -0700634 exposesIndex++;
635 jsonPointerPath = jsonPointerPathBoard;
636 jsonPointerPath += std::to_string(exposesIndex);
637
James Feistd63d18a2018-07-19 15:23:45 -0700638 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800639 if (findName == item.end())
640 {
641 std::cerr << "cannot find name in field " << item << "\n";
642 continue;
643 }
James Feist1e3e6982018-08-03 16:09:28 -0700644 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800645 // if status is not found it is assumed to be status = 'okay'
646 if (findStatus != item.end())
647 {
648 if (*findStatus == "disabled")
649 {
650 continue;
651 }
652 }
James Feistd63d18a2018-07-19 15:23:45 -0700653 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800654 std::string itemType;
655 if (findType != item.end())
656 {
657 itemType = findType->get<std::string>();
658 std::regex_replace(itemType.begin(), itemType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800659 itemType.end(), illegalDbusPathRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800660 }
661 else
662 {
663 itemType = "unknown";
664 }
665 std::string itemName = findName->get<std::string>();
666 std::regex_replace(itemName.begin(), itemName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800667 itemName.end(), illegalDbusMemberRegex, "_");
668 std::string ifacePath = boardName;
669 ifacePath += "/";
670 ifacePath += itemName;
James Feistc6248a52018-08-14 10:09:45 -0700671
James Feistd58879a82019-09-11 11:26:07 -0700672 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
Ed Tanous07d467b2021-02-23 14:48:37 -0800673 createInterface(objServer, ifacePath,
James Feistd58879a82019-09-11 11:26:07 -0700674 "xyz.openbmc_project.Configuration." + itemType,
675 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -0800676
James Feist97a63f12018-05-17 13:50:57 -0700677 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700678 itemIface, item, objServer,
679 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -0800680
James Feista465ccc2019-02-08 12:51:01 -0800681 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800682 {
James Feist97a63f12018-05-17 13:50:57 -0700683 jsonPointerPath = jsonPointerPathBoard +
684 std::to_string(exposesIndex) + "/" +
685 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -0800686 if (objectPair.value().type() ==
687 nlohmann::json::value_t::object)
688 {
James Feistd58879a82019-09-11 11:26:07 -0700689 std::shared_ptr<sdbusplus::asio::dbus_interface>
690 objectIface = createInterface(
Ed Tanous07d467b2021-02-23 14:48:37 -0800691 objServer, ifacePath,
James Feistd58879a82019-09-11 11:26:07 -0700692 "xyz.openbmc_project.Configuration." + itemType +
693 "." + objectPair.key(),
694 boardKeyOrig);
James Feist97a63f12018-05-17 13:50:57 -0700695
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +0200696 populateInterfaceFromJson(systemConfiguration,
697 jsonPointerPath, objectIface,
698 objectPair.value(), objServer,
699 getPermission(objectPair.key()));
James Feist1b2e2242018-01-30 13:45:19 -0800700 }
701 else if (objectPair.value().type() ==
702 nlohmann::json::value_t::array)
703 {
704 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -0700705 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -0800706 {
James Feist8f2710a2018-05-09 17:18:55 -0700707 continue;
708 }
709 bool isLegal = true;
710 auto type = objectPair.value()[0].type();
711 if (type != nlohmann::json::value_t::object)
712 {
713 continue;
714 }
715
716 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -0800717 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700718 {
719 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800720 {
James Feist8f2710a2018-05-09 17:18:55 -0700721 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800722 break;
723 }
James Feist8f2710a2018-05-09 17:18:55 -0700724 }
725 if (!isLegal)
726 {
727 std::cerr << "dbus format error" << objectPair.value()
728 << "\n";
729 break;
730 }
731
James Feista465ccc2019-02-08 12:51:01 -0800732 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700733 {
James Feist97a63f12018-05-17 13:50:57 -0700734
James Feistd58879a82019-09-11 11:26:07 -0700735 std::shared_ptr<sdbusplus::asio::dbus_interface>
736 objectIface = createInterface(
Ed Tanous07d467b2021-02-23 14:48:37 -0800737 objServer, ifacePath,
James Feistd58879a82019-09-11 11:26:07 -0700738 "xyz.openbmc_project.Configuration." +
739 itemType + "." + objectPair.key() +
740 std::to_string(index),
741 boardKeyOrig);
742
James Feistc6248a52018-08-14 10:09:45 -0700743 populateInterfaceFromJson(
744 systemConfiguration,
745 jsonPointerPath + "/" + std::to_string(index),
746 objectIface, arrayItem, objServer,
747 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -0700748 index++;
James Feist1b2e2242018-01-30 13:45:19 -0800749 }
750 }
751 }
752 }
753 }
754}
755
James Feist8f2710a2018-05-09 17:18:55 -0700756// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -0800757bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -0800758{
759 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -0800760 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -0700761 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
762 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -0800763 {
764 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -0700765 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -0800766 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800767 }
James Feistb4383f42018-08-06 16:54:10 -0700768
769 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
770 globalSchema);
771 if (!schemaStream.good())
772 {
773 std::cerr
774 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
775 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -0800776 return false;
James Feistb4383f42018-08-06 16:54:10 -0700777 }
778 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
779 if (schema.is_discarded())
780 {
781 std::cerr
782 << "Illegal schema file detected, cannot validate JSON, exiting\n";
783 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -0800784 return false;
James Feistb4383f42018-08-06 16:54:10 -0700785 }
786
James Feista465ccc2019-02-08 12:51:01 -0800787 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -0800788 {
789 std::ifstream jsonStream(jsonPath.c_str());
790 if (!jsonStream.good())
791 {
792 std::cerr << "unable to open " << jsonPath.string() << "\n";
793 continue;
794 }
795 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
796 if (data.is_discarded())
797 {
798 std::cerr << "syntax error in " << jsonPath.string() << "\n";
799 continue;
800 }
James Feist8da99192019-01-24 08:20:16 -0800801 /*
802 * todo(james): reenable this once less things are in flight
803 *
James Feistb4383f42018-08-06 16:54:10 -0700804 if (!validateJson(schema, data))
805 {
806 std::cerr << "Error validating " << jsonPath.string() << "\n";
807 continue;
808 }
James Feist8da99192019-01-24 08:20:16 -0800809 */
James Feistb4383f42018-08-06 16:54:10 -0700810
James Feist3cb5fec2018-01-23 14:41:51 -0800811 if (data.type() == nlohmann::json::value_t::array)
812 {
James Feista465ccc2019-02-08 12:51:01 -0800813 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -0800814 {
815 configurations.emplace_back(d);
816 }
817 }
818 else
819 {
820 configurations.emplace_back(data);
821 }
822 }
Ed Tanous072e25d2018-12-16 21:45:20 -0800823 return true;
James Feist75fdeeb2018-02-20 14:26:16 -0800824}
James Feist3cb5fec2018-01-23 14:41:51 -0800825
James Feistb1728ca2020-04-30 15:40:55 -0700826void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -0700827 nlohmann::json& systemConfiguration)
828{
829 static bool scannedPowerOff = false;
830 static bool scannedPowerOn = false;
831
James Feistfb00f392019-06-25 14:16:48 -0700832 if (systemConfiguration.empty() || lastJson.empty())
833 {
834 return; // not ready yet
835 }
James Feist1df06a42019-04-11 14:23:04 -0700836 if (scannedPowerOn)
837 {
838 return;
839 }
840
841 if (!isPowerOn() && scannedPowerOff)
842 {
843 return;
844 }
845
James Feistb1728ca2020-04-30 15:40:55 -0700846 timer.expires_after(std::chrono::seconds(10));
James Feist1a996582019-05-14 15:10:06 -0700847 timer.async_wait(
848 [&systemConfiguration](const boost::system::error_code& ec) {
849 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -0700850 {
James Feist1a996582019-05-14 15:10:06 -0700851 // we were cancelled
852 return;
853 }
854
855 bool powerOff = !isPowerOn();
856 for (const auto& item : lastJson.items())
857 {
858 if (systemConfiguration.find(item.key()) ==
859 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -0700860 {
James Feist1a996582019-05-14 15:10:06 -0700861 bool isDetectedPowerOn = false;
862 auto powerState = item.value().find("PowerState");
863 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -0700864 {
James Feist1a996582019-05-14 15:10:06 -0700865 auto ptr = powerState->get_ptr<const std::string*>();
866 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -0700867 {
James Feist1a996582019-05-14 15:10:06 -0700868 if (*ptr == "On" || *ptr == "BiosPost")
869 {
870 isDetectedPowerOn = true;
871 }
James Feist1df06a42019-04-11 14:23:04 -0700872 }
873 }
James Feist1a996582019-05-14 15:10:06 -0700874 if (powerOff && isDetectedPowerOn)
875 {
876 // power not on yet, don't know if it's there or not
877 continue;
878 }
879 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
880 {
881 // already logged it when power was off
882 continue;
883 }
James Feist1df06a42019-04-11 14:23:04 -0700884
James Feist1a996582019-05-14 15:10:06 -0700885 logDeviceRemoved(item.value());
886 }
James Feist1df06a42019-04-11 14:23:04 -0700887 }
James Feist1a996582019-05-14 15:10:06 -0700888 scannedPowerOff = true;
889 if (!powerOff)
890 {
891 scannedPowerOn = true;
892 }
893 });
James Feist1df06a42019-04-11 14:23:04 -0700894}
895
James Feist8f2710a2018-05-09 17:18:55 -0700896// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -0700897void propertiesChangedCallback(nlohmann::json& systemConfiguration,
898 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -0700899{
James Feist2539ccd2020-05-01 16:15:08 -0700900 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -0700901 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -0700902 static size_t instance = 0;
903 instance++;
904 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -0700905
James Feistb1728ca2020-04-30 15:40:55 -0700906 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -0700907
908 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -0700909 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -0700910 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -0700911 if (ec == boost::asio::error::operation_aborted)
912 {
913 // we were cancelled
914 return;
915 }
Ed Tanous07d467b2021-02-23 14:48:37 -0800916 if (ec)
James Feist8f2710a2018-05-09 17:18:55 -0700917 {
918 std::cerr << "async wait error " << ec << "\n";
919 return;
920 }
921
James Feist2539ccd2020-05-01 16:15:08 -0700922 if (inProgress)
923 {
924 propertiesChangedCallback(systemConfiguration, objServer);
925 return;
926 }
927 inProgress = true;
928
James Feist8f2710a2018-05-09 17:18:55 -0700929 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -0700930 auto missingConfigurations = std::make_shared<nlohmann::json>();
931 *missingConfigurations = systemConfiguration;
932
James Feist8f2710a2018-05-09 17:18:55 -0700933 std::list<nlohmann::json> configurations;
934 if (!findJsonFiles(configurations))
935 {
936 std::cerr << "cannot find json files\n";
James Feist2539ccd2020-05-01 16:15:08 -0700937 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -0700938 return;
939 }
940
941 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -0700942 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -0700943 objServer,
944 [&systemConfiguration, &objServer, count, oldConfiguration,
945 missingConfigurations]() {
James Feist899e17f2019-09-13 11:46:29 -0700946 // this is something that since ac has been applied to the bmc
947 // we saw, and we no longer see it
948 bool powerOff = !isPowerOn();
949 for (const auto& item : missingConfigurations->items())
950 {
951 bool isDetectedPowerOn = false;
952 auto powerState = item.value().find("PowerState");
953 if (powerState != item.value().end())
954 {
955 auto ptr = powerState->get_ptr<const std::string*>();
956 if (ptr)
957 {
958 if (*ptr == "On" || *ptr == "BiosPost")
959 {
960 isDetectedPowerOn = true;
961 }
962 }
963 }
964 if (powerOff && isDetectedPowerOn)
965 {
966 // power not on yet, don't know if it's there or not
967 continue;
968 }
969 std::string name = item.value()["Name"].get<std::string>();
James Feist02d2b932020-02-06 16:28:48 -0800970 std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
James Feist899e17f2019-09-13 11:46:29 -0700971 ifaces = inventory[name];
972 for (auto& iface : ifaces)
973 {
James Feist02d2b932020-02-06 16:28:48 -0800974 auto sharedPtr = iface.lock();
975 if (!sharedPtr)
976 {
977 continue; // was already deleted elsewhere
978 }
979 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -0700980 }
981 ifaces.clear();
982 systemConfiguration.erase(item.key());
983 logDeviceRemoved(item.value());
984 }
985
James Feist8f2710a2018-05-09 17:18:55 -0700986 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -0800987 for (auto it = newConfiguration.begin();
988 it != newConfiguration.end();)
989 {
990 auto findKey = oldConfiguration.find(it.key());
991 if (findKey != oldConfiguration.end())
992 {
993 it = newConfiguration.erase(it);
994 }
995 else
996 {
997 it++;
998 }
999 }
James Feist899e17f2019-09-13 11:46:29 -07001000 for (const auto& item : newConfiguration.items())
1001 {
1002 logDeviceAdded(item.value());
1003 }
1004
James Feist2539ccd2020-05-01 16:15:08 -07001005 inProgress = false;
1006
Jonathan Doman6d649822021-05-05 16:53:04 -07001007 io.post([count, newConfiguration, &systemConfiguration,
1008 &objServer]() {
James Feist8f2710a2018-05-09 17:18:55 -07001009 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001010
Jonathan Doman6d649822021-05-05 16:53:04 -07001011 io.post([&systemConfiguration]() {
James Feistbb43d022018-06-12 15:44:33 -07001012 if (!writeJsonFiles(systemConfiguration))
1013 {
1014 std::cerr << "Error writing json files\n";
1015 }
1016 });
Jonathan Doman6d649822021-05-05 16:53:04 -07001017 io.post([count, newConfiguration, &systemConfiguration,
1018 &objServer]() {
James Feist97a63f12018-05-17 13:50:57 -07001019 postToDbus(newConfiguration, systemConfiguration,
1020 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001021 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001022 {
James Feist899e17f2019-09-13 11:46:29 -07001023 return;
James Feist1df06a42019-04-11 14:23:04 -07001024 }
James Feist899e17f2019-09-13 11:46:29 -07001025 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001026 });
1027 });
1028 });
1029 perfScan->run();
1030 });
James Feist75fdeeb2018-02-20 14:26:16 -08001031}
1032
James Feist98132792019-07-09 13:29:09 -07001033int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001034{
1035 // setup connection to dbus
Ed Tanous07d467b2021-02-23 14:48:37 -08001036 systemBus = std::make_shared<sdbusplus::asio::connection>(io);
1037 systemBus->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001038
Ed Tanous07d467b2021-02-23 14:48:37 -08001039 sdbusplus::asio::object_server objServer(systemBus);
James Feistfd1264a2018-05-03 12:10:00 -07001040
James Feist8f2710a2018-05-09 17:18:55 -07001041 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1042 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1043 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001044
James Feist4131aea2018-03-09 09:47:30 -08001045 // to keep reference to the match / filter objects so they don't get
1046 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001047
1048 nlohmann::json systemConfiguration = nlohmann::json::object();
1049
Brad Bishopc76af0f2020-12-04 13:50:23 -05001050 // We need a poke from DBus for static providers that create all their
1051 // objects prior to claiming a well-known name, and thus don't emit any
1052 // org.freedesktop.DBus.Properties signals. Similarly if a process exits
1053 // for any reason, expected or otherwise, we'll need a poke to remove
1054 // entities from DBus.
1055 sdbusplus::bus::match::match nameOwnerChangedMatch(
Ed Tanous07d467b2021-02-23 14:48:37 -08001056 static_cast<sdbusplus::bus::bus&>(*systemBus),
Brad Bishopc76af0f2020-12-04 13:50:23 -05001057 sdbusplus::bus::match::rules::nameOwnerChanged(),
1058 [&](sdbusplus::message::message&) {
1059 propertiesChangedCallback(systemConfiguration, objServer);
1060 });
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001061 // We also need a poke from DBus when new interfaces are created or
1062 // destroyed.
1063 sdbusplus::bus::match::match interfacesAddedMatch(
Ed Tanous07d467b2021-02-23 14:48:37 -08001064 static_cast<sdbusplus::bus::bus&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001065 sdbusplus::bus::match::rules::interfacesAdded(),
1066 [&](sdbusplus::message::message&) {
1067 propertiesChangedCallback(systemConfiguration, objServer);
1068 });
1069 sdbusplus::bus::match::match interfacesRemovedMatch(
Ed Tanous07d467b2021-02-23 14:48:37 -08001070 static_cast<sdbusplus::bus::bus&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001071 sdbusplus::bus::match::rules::interfacesRemoved(),
1072 [&](sdbusplus::message::message&) {
1073 propertiesChangedCallback(systemConfiguration, objServer);
1074 });
Brad Bishopc76af0f2020-12-04 13:50:23 -05001075
James Feist4dc617b2020-05-01 09:54:47 -07001076 io.post(
1077 [&]() { propertiesChangedCallback(systemConfiguration, objServer); });
James Feist4131aea2018-03-09 09:47:30 -08001078
James Feistfd1264a2018-05-03 12:10:00 -07001079 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001080 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001081 });
James Feist8f2710a2018-05-09 17:18:55 -07001082 entityIface->initialize();
1083
James Feist1df06a42019-04-11 14:23:04 -07001084 if (fwVersionIsSame())
1085 {
1086 if (std::filesystem::is_regular_file(currentConfiguration))
1087 {
1088 // this file could just be deleted, but it's nice for debug
1089 std::filesystem::create_directory(tempConfigDir);
1090 std::filesystem::remove(lastConfiguration);
1091 std::filesystem::copy(currentConfiguration, lastConfiguration);
1092 std::filesystem::remove(currentConfiguration);
1093
1094 std::ifstream jsonStream(lastConfiguration);
1095 if (jsonStream.good())
1096 {
1097 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1098 if (data.is_discarded())
1099 {
1100 std::cerr << "syntax error in " << lastConfiguration
1101 << "\n";
1102 }
1103 else
1104 {
1105 lastJson = std::move(data);
1106 }
1107 }
1108 else
1109 {
1110 std::cerr << "unable to open " << lastConfiguration << "\n";
1111 }
1112 }
1113 }
1114 else
1115 {
1116 // not an error, just logging at this level to make it in the journal
1117 std::cerr << "Clearing previous configuration\n";
1118 std::filesystem::remove(currentConfiguration);
1119 }
1120
1121 // some boards only show up after power is on, we want to not say they are
1122 // removed until the same state happens
Ed Tanous07d467b2021-02-23 14:48:37 -08001123 setupPowerMatch(systemBus);
James Feist1df06a42019-04-11 14:23:04 -07001124
James Feist1b2e2242018-01-30 13:45:19 -08001125 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001126
1127 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001128}