blob: d327f2bd36daa4baa42ab039a7dc0e66bb43354d [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>
Andrew Jeffery666583b2021-12-01 15:50:12 +103042#include <map>
James Feista465ccc2019-02-08 12:51:01 -080043#include <regex>
James Feista465ccc2019-02-08 12:51:01 -080044#include <variant>
Andrew Jefferya9c58922021-06-01 09:28:59 +093045constexpr const char* hostConfigurationDirectory = SYSCONF_DIR "configurations";
James Feista465ccc2019-02-08 12:51:01 -080046constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
47constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070048constexpr const char* tempConfigDir = "/tmp/configuration/";
49constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
50constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080051constexpr const char* globalSchema = "global.json";
Ed Tanous07d467b2021-02-23 14:48:37 -080052constexpr const bool debug = false;
James Feistf1b14142019-04-10 15:22:09 -070053
Andrew Jeffery666583b2021-12-01 15:50:12 +103054const boost::container::flat_map<const char*, probe_type_codes, CmpStr>
Ed Tanous07d467b2021-02-23 14:48:37 -080055 probeTypes{{{"FALSE", probe_type_codes::FALSE_T},
56 {"TRUE", probe_type_codes::TRUE_T},
57 {"AND", probe_type_codes::AND},
58 {"OR", probe_type_codes::OR},
59 {"FOUND", probe_type_codes::FOUND},
60 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080061
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020062static constexpr std::array<const char*, 6> settableInterfaces = {
63 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist68500ff2018-08-08 15:40:42 -070064using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080065 std::variant<std::vector<std::string>, std::vector<double>, std::string,
66 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
67 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080068
69using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070070 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080071 boost::container::flat_map<
72 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070073 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080074
James Feistd58879a2019-09-11 11:26:07 -070075// store reference to all interfaces so we can destroy them later
76boost::container::flat_map<
James Feist02d2b932020-02-06 16:28:48 -080077 std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
James Feistd58879a2019-09-11 11:26:07 -070078 inventory;
79
James Feist3cb5fec2018-01-23 14:41:51 -080080// todo: pass this through nicer
Ed Tanous07d467b2021-02-23 14:48:37 -080081std::shared_ptr<sdbusplus::asio::connection> systemBus;
Andrew Jeffery47af65a2021-12-01 14:16:31 +103082nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -080083
James Feist02d2b932020-02-06 16:28:48 -080084boost::asio::io_context io;
85
Ed Tanous07d467b2021-02-23 14:48:37 -080086const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
87const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -080088
Andrew Jeffery666583b2021-12-01 15:50:12 +103089FoundProbeTypeT findProbeType(const std::string& probe)
90{
91 boost::container::flat_map<const char*, probe_type_codes,
92 CmpStr>::const_iterator probeType;
93 for (probeType = probeTypes.begin(); probeType != probeTypes.end();
94 ++probeType)
95 {
96 if (probe.find(probeType->first) != std::string::npos)
97 {
98 return probeType;
99 }
100 }
101
102 return std::nullopt;
103}
104
James Feistd58879a2019-09-11 11:26:07 -0700105static std::shared_ptr<sdbusplus::asio::dbus_interface>
106 createInterface(sdbusplus::asio::object_server& objServer,
107 const std::string& path, const std::string& interface,
James Feist02d2b932020-02-06 16:28:48 -0800108 const std::string& parent, bool checkNull = false)
James Feistd58879a2019-09-11 11:26:07 -0700109{
James Feist02d2b932020-02-06 16:28:48 -0800110 // on first add we have no reason to check for null before add, as there
111 // won't be any. For dynamically added interfaces, we check for null so that
112 // a constant delete/add will not create a memory leak
113
114 auto ptr = objServer.add_interface(path, interface);
115 auto& dataVector = inventory[parent];
116 if (checkNull)
117 {
118 auto it = std::find_if(dataVector.begin(), dataVector.end(),
119 [](const auto& p) { return p.expired(); });
120 if (it != dataVector.end())
121 {
122 *it = ptr;
123 return ptr;
124 }
125 }
126 dataVector.emplace_back(ptr);
127 return ptr;
James Feistd58879a2019-09-11 11:26:07 -0700128}
129
James Feist8f2710a2018-05-09 17:18:55 -0700130// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800131bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800132{
James Feist1df06a42019-04-11 14:23:04 -0700133 std::filesystem::create_directory(configurationOutDir);
134 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700135 if (!output.good())
136 {
137 return false;
138 }
James Feist1b2e2242018-01-30 13:45:19 -0800139 output << systemConfiguration.dump(4);
140 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700141 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700142}
James Feist1b2e2242018-01-30 13:45:19 -0800143
James Feist97a63f12018-05-17 13:50:57 -0700144template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800145bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
146 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700147{
148 try
149 {
150 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800151 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700152 ref = value;
153 return true;
154 }
James Feist98132792019-07-09 13:29:09 -0700155 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700156 {
157 return false;
158 }
159}
James Feistbb43d022018-06-12 15:44:33 -0700160
James Feistebcc26b2019-03-22 12:30:43 -0700161// template function to add array as dbus property
162template <typename PropertyType>
163void addArrayToDbus(const std::string& name, const nlohmann::json& array,
164 sdbusplus::asio::dbus_interface* iface,
165 sdbusplus::asio::PropertyPermission permission,
166 nlohmann::json& systemConfiguration,
167 const std::string& jsonPointerString)
168{
169 std::vector<PropertyType> values;
170 for (const auto& property : array)
171 {
172 auto ptr = property.get_ptr<const PropertyType*>();
173 if (ptr != nullptr)
174 {
175 values.emplace_back(*ptr);
176 }
177 }
178
179 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
180 {
181 iface->register_property(name, values);
182 }
183 else
184 {
185 iface->register_property(
186 name, values,
187 [&systemConfiguration,
188 jsonPointerString{std::string(jsonPointerString)}](
189 const std::vector<PropertyType>& newVal,
190 std::vector<PropertyType>& val) {
191 val = newVal;
192 if (!setJsonFromPointer(jsonPointerString, val,
193 systemConfiguration))
194 {
195 std::cerr << "error setting json field\n";
196 return -1;
197 }
198 if (!writeJsonFiles(systemConfiguration))
199 {
200 std::cerr << "error setting json file\n";
201 return -1;
202 }
203 return 1;
204 });
205 }
206}
207
James Feistbb43d022018-06-12 15:44:33 -0700208template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800209void addProperty(const std::string& propertyName, const PropertyType& value,
210 sdbusplus::asio::dbus_interface* iface,
211 nlohmann::json& systemConfiguration,
212 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700213 sdbusplus::asio::PropertyPermission permission)
214{
215 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
216 {
217 iface->register_property(propertyName, value);
218 return;
219 }
James Feist68500ff2018-08-08 15:40:42 -0700220 iface->register_property(
221 propertyName, value,
222 [&systemConfiguration,
223 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800224 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700225 val = newVal;
226 if (!setJsonFromPointer(jsonPointerString, val,
227 systemConfiguration))
228 {
229 std::cerr << "error setting json field\n";
230 return -1;
231 }
James Feistc6248a52018-08-14 10:09:45 -0700232 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700233 {
234 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700235 return -1;
236 }
237 return 1;
238 });
239}
240
241void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800242 const std::string& jsonPointerPath,
243 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
244 sdbusplus::asio::object_server& objServer,
245 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700246{
247 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
248 iface->register_method(
249 "Delete", [&objServer, &systemConfiguration, interface,
250 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700251 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700252 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700253 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700254 {
255 // this technically can't happen as the pointer is pointing to
256 // us
257 throw DBusInternalError();
258 }
259 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feistc6248a52018-08-14 10:09:45 -0700260 systemConfiguration[ptr] = nullptr;
261
James Feist02d2b932020-02-06 16:28:48 -0800262 // todo(james): dig through sdbusplus to find out why we can't
263 // delete it in a method call
264 io.post([&objServer, dbusInterface]() mutable {
265 objServer.remove_interface(dbusInterface);
266 });
267
James Feistc6248a52018-08-14 10:09:45 -0700268 if (!writeJsonFiles(systemConfiguration))
269 {
270 std::cerr << "error setting json file\n";
271 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700272 }
James Feist68500ff2018-08-08 15:40:42 -0700273 });
James Feistbb43d022018-06-12 15:44:33 -0700274}
275
James Feist1b2e2242018-01-30 13:45:19 -0800276// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700277void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800278 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
279 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
280 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700281 sdbusplus::asio::PropertyPermission permission =
282 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800283{
James Feista465ccc2019-02-08 12:51:01 -0800284 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800285 {
James Feist8f2710a2018-05-09 17:18:55 -0700286 auto type = dictPair.value().type();
287 bool array = false;
288 if (dictPair.value().type() == nlohmann::json::value_t::array)
289 {
290 array = true;
291 if (!dictPair.value().size())
292 {
293 continue;
294 }
295 type = dictPair.value()[0].type();
296 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800297 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700298 {
299 if (arrayItem.type() != type)
300 {
301 isLegal = false;
302 break;
303 }
304 }
305 if (!isLegal)
306 {
307 std::cerr << "dbus format error" << dictPair.value() << "\n";
308 continue;
309 }
James Feista218ddb2019-04-11 14:01:31 -0700310 }
311 if (type == nlohmann::json::value_t::object)
312 {
313 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700314 }
James Feist97a63f12018-05-17 13:50:57 -0700315 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700316 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
317 {
318 // all setable numbers are doubles as it is difficult to always
319 // create a configuration file with all whole numbers as decimals
320 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700321 if (array)
322 {
323 if (dictPair.value()[0].is_number())
324 {
325 type = nlohmann::json::value_t::number_float;
326 }
327 }
328 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700329 {
330 type = nlohmann::json::value_t::number_float;
331 }
332 }
333
James Feist8f2710a2018-05-09 17:18:55 -0700334 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800335 {
James Feist9eb0b582018-04-27 12:15:46 -0700336 case (nlohmann::json::value_t::boolean):
337 {
James Feist8f2710a2018-05-09 17:18:55 -0700338 if (array)
339 {
340 // todo: array of bool isn't detected correctly by
341 // sdbusplus, change it to numbers
342 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700343 iface.get(), permission,
344 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700345 }
James Feistbb43d022018-06-12 15:44:33 -0700346
James Feist97a63f12018-05-17 13:50:57 -0700347 else
348 {
James Feistbb43d022018-06-12 15:44:33 -0700349 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700350 iface.get(), systemConfiguration, key,
351 permission);
James Feist97a63f12018-05-17 13:50:57 -0700352 }
James Feist9eb0b582018-04-27 12:15:46 -0700353 break;
354 }
355 case (nlohmann::json::value_t::number_integer):
356 {
James Feist8f2710a2018-05-09 17:18:55 -0700357 if (array)
358 {
359 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700360 iface.get(), permission,
361 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700362 }
363 else
364 {
James Feistbb43d022018-06-12 15:44:33 -0700365 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700366 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700367 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700368 }
James Feist9eb0b582018-04-27 12:15:46 -0700369 break;
370 }
371 case (nlohmann::json::value_t::number_unsigned):
372 {
James Feist8f2710a2018-05-09 17:18:55 -0700373 if (array)
374 {
375 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700376 iface.get(), permission,
377 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700378 }
379 else
380 {
James Feistbb43d022018-06-12 15:44:33 -0700381 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700382 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700383 systemConfiguration, key,
384 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700385 }
James Feist9eb0b582018-04-27 12:15:46 -0700386 break;
387 }
388 case (nlohmann::json::value_t::number_float):
389 {
James Feist8f2710a2018-05-09 17:18:55 -0700390 if (array)
391 {
392 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700393 iface.get(), permission,
394 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700395 }
James Feistbb43d022018-06-12 15:44:33 -0700396
James Feist97a63f12018-05-17 13:50:57 -0700397 else
398 {
James Feistbb43d022018-06-12 15:44:33 -0700399 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700400 iface.get(), systemConfiguration, key,
401 permission);
James Feist97a63f12018-05-17 13:50:57 -0700402 }
James Feist9eb0b582018-04-27 12:15:46 -0700403 break;
404 }
405 case (nlohmann::json::value_t::string):
406 {
James Feist8f2710a2018-05-09 17:18:55 -0700407 if (array)
408 {
James Feistebcc26b2019-03-22 12:30:43 -0700409 addArrayToDbus<std::string>(
410 dictPair.key(), dictPair.value(), iface.get(),
411 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700412 }
413 else
414 {
James Feistc6248a52018-08-14 10:09:45 -0700415 addProperty(
416 dictPair.key(), dictPair.value().get<std::string>(),
417 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700418 }
James Feist9eb0b582018-04-27 12:15:46 -0700419 break;
420 }
James Feist0eb40352019-04-09 14:44:04 -0700421 default:
422 {
James Feista218ddb2019-04-11 14:01:31 -0700423 std::cerr << "Unexpected json type in system configuration "
424 << dictPair.key() << ": "
425 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700426 break;
427 }
James Feist1b2e2242018-01-30 13:45:19 -0800428 }
429 }
James Feistc6248a52018-08-14 10:09:45 -0700430 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
431 {
432 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
433 systemConfiguration);
434 }
James Feist8f2710a2018-05-09 17:18:55 -0700435 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800436}
437
James Feista465ccc2019-02-08 12:51:01 -0800438sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700439{
440 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
441 interface) != settableInterfaces.end()
442 ? sdbusplus::asio::PropertyPermission::readWrite
443 : sdbusplus::asio::PropertyPermission::readOnly;
444}
445
James Feista465ccc2019-02-08 12:51:01 -0800446void createAddObjectMethod(const std::string& jsonPointerPath,
447 const std::string& path,
448 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700449 sdbusplus::asio::object_server& objServer,
450 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700451{
James Feistd58879a2019-09-11 11:26:07 -0700452 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
453 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700454
455 iface->register_method(
456 "AddObject",
457 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700458 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
459 board](const boost::container::flat_map<std::string, JsonVariantType>&
460 data) {
James Feist68500ff2018-08-08 15:40:42 -0700461 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800462 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700463 auto findExposes = base.find("Exposes");
464
465 if (findExposes == base.end())
466 {
467 throw std::invalid_argument("Entity must have children.");
468 }
469
470 // this will throw invalid-argument to sdbusplus if invalid json
471 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800472 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700473 {
James Feista465ccc2019-02-08 12:51:01 -0800474 nlohmann::json& newJson = newData[item.first];
475 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
476 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700477 }
478
479 auto findName = newData.find("Name");
480 auto findType = newData.find("Type");
481 if (findName == newData.end() || findType == newData.end())
482 {
483 throw std::invalid_argument("AddObject missing Name or Type");
484 }
James Feista465ccc2019-02-08 12:51:01 -0800485 const std::string* type = findType->get_ptr<const std::string*>();
486 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700487 if (type == nullptr || name == nullptr)
488 {
489 throw std::invalid_argument("Type and Name must be a string.");
490 }
491
James Feist02d2b932020-02-06 16:28:48 -0800492 bool foundNull = false;
James Feist68500ff2018-08-08 15:40:42 -0700493 size_t lastIndex = 0;
494 // we add in the "exposes"
James Feist02d2b932020-02-06 16:28:48 -0800495 for (const auto& expose : *findExposes)
James Feist68500ff2018-08-08 15:40:42 -0700496 {
James Feist02d2b932020-02-06 16:28:48 -0800497 if (expose.is_null())
498 {
499 foundNull = true;
500 continue;
501 }
502
503 if (expose["Name"] == *name && expose["Type"] == *type)
James Feist68500ff2018-08-08 15:40:42 -0700504 {
505 throw std::invalid_argument(
506 "Field already in JSON, not adding");
507 }
James Feist02d2b932020-02-06 16:28:48 -0800508
509 if (foundNull)
510 {
511 continue;
512 }
513
James Feist68500ff2018-08-08 15:40:42 -0700514 lastIndex++;
515 }
516
517 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
518 *type + ".json");
519 // todo(james) we might want to also make a list of 'can add'
520 // interfaces but for now I think the assumption if there is a
521 // schema avaliable that it is allowed to update is fine
522 if (!schemaFile.good())
523 {
524 throw std::invalid_argument(
525 "No schema avaliable, cannot validate.");
526 }
527 nlohmann::json schema =
528 nlohmann::json::parse(schemaFile, nullptr, false);
529 if (schema.is_discarded())
530 {
531 std::cerr << "Schema not legal" << *type << ".json\n";
532 throw DBusInternalError();
533 }
534 if (!validateJson(schema, newData))
535 {
536 throw std::invalid_argument("Data does not match schema");
537 }
James Feist02d2b932020-02-06 16:28:48 -0800538 if (foundNull)
539 {
540 findExposes->at(lastIndex) = newData;
541 }
542 else
543 {
544 findExposes->push_back(newData);
545 }
James Feist68500ff2018-08-08 15:40:42 -0700546 if (!writeJsonFiles(systemConfiguration))
547 {
548 std::cerr << "Error writing json files\n";
549 throw DBusInternalError();
550 }
551 std::string dbusName = *name;
552
553 std::regex_replace(dbusName.begin(), dbusName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800554 dbusName.end(), illegalDbusMemberRegex, "_");
James Feistd58879a2019-09-11 11:26:07 -0700555
556 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
557 createInterface(objServer, path + "/" + dbusName,
558 "xyz.openbmc_project.Configuration." + *type,
James Feist02d2b932020-02-06 16:28:48 -0800559 board, true);
James Feist68500ff2018-08-08 15:40:42 -0700560 // permission is read-write, as since we just created it, must be
561 // runtime modifiable
562 populateInterfaceFromJson(
563 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700564 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700565 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700566 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700567 });
568 iface->initialize();
569}
570
James Feista465ccc2019-02-08 12:51:01 -0800571void postToDbus(const nlohmann::json& newConfiguration,
572 nlohmann::json& systemConfiguration,
573 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800574
James Feist1b2e2242018-01-30 13:45:19 -0800575{
James Feist97a63f12018-05-17 13:50:57 -0700576 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800577 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800578 {
James Feistf1b14142019-04-10 15:22:09 -0700579 std::string boardKey = boardPair.value()["Name"];
James Feistd58879a2019-09-11 11:26:07 -0700580 std::string boardKeyOrig = boardPair.value()["Name"];
James Feistf1b14142019-04-10 15:22:09 -0700581 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700582 // loop through newConfiguration, but use values from system
583 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700584 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700585 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800586 std::string boardType;
587 if (findBoardType != boardValues.end() &&
588 findBoardType->type() == nlohmann::json::value_t::string)
589 {
590 boardType = findBoardType->get<std::string>();
591 std::regex_replace(boardType.begin(), boardType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800592 boardType.end(), illegalDbusMemberRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800593 }
594 else
595 {
596 std::cerr << "Unable to find type for " << boardKey
597 << " reverting to Chassis.\n";
598 boardType = "Chassis";
599 }
James Feist11be6672018-04-06 14:05:32 -0700600 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800601
602 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800603 illegalDbusMemberRegex, "_");
604 std::string boardName = "/xyz/openbmc_project/inventory/system/";
605 boardName += boardtypeLower;
606 boardName += "/";
607 boardName += boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800608
James Feistd58879a2019-09-11 11:26:07 -0700609 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
610 createInterface(objServer, boardName,
611 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700612
James Feistd58879a2019-09-11 11:26:07 -0700613 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
614 createInterface(objServer, boardName,
615 "xyz.openbmc_project.Inventory.Item." + boardType,
616 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -0700617
James Feist68500ff2018-08-08 15:40:42 -0700618 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700619 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -0700620
James Feist97a63f12018-05-17 13:50:57 -0700621 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700622 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700623 jsonPointerPath += "/";
624 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800625 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700626 {
627 if (boardField.value().type() == nlohmann::json::value_t::object)
628 {
James Feistd58879a2019-09-11 11:26:07 -0700629 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
630 createInterface(objServer, boardName, boardField.key(),
631 boardKeyOrig);
632
James Feistc6248a52018-08-14 10:09:45 -0700633 populateInterfaceFromJson(systemConfiguration,
634 jsonPointerPath + boardField.key(),
635 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700636 }
637 }
James Feist97a63f12018-05-17 13:50:57 -0700638
James Feist1e3e6982018-08-03 16:09:28 -0700639 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800640 if (exposes == boardValues.end())
641 {
642 continue;
643 }
James Feist97a63f12018-05-17 13:50:57 -0700644 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700645 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700646
647 // store the board level pointer so we can modify it on the way down
648 std::string jsonPointerPathBoard = jsonPointerPath;
649 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -0800650 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -0800651 {
James Feist97a63f12018-05-17 13:50:57 -0700652 exposesIndex++;
653 jsonPointerPath = jsonPointerPathBoard;
654 jsonPointerPath += std::to_string(exposesIndex);
655
James Feistd63d18a2018-07-19 15:23:45 -0700656 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800657 if (findName == item.end())
658 {
659 std::cerr << "cannot find name in field " << item << "\n";
660 continue;
661 }
James Feist1e3e6982018-08-03 16:09:28 -0700662 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800663 // if status is not found it is assumed to be status = 'okay'
664 if (findStatus != item.end())
665 {
666 if (*findStatus == "disabled")
667 {
668 continue;
669 }
670 }
James Feistd63d18a2018-07-19 15:23:45 -0700671 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800672 std::string itemType;
673 if (findType != item.end())
674 {
675 itemType = findType->get<std::string>();
676 std::regex_replace(itemType.begin(), itemType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800677 itemType.end(), illegalDbusPathRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800678 }
679 else
680 {
681 itemType = "unknown";
682 }
683 std::string itemName = findName->get<std::string>();
684 std::regex_replace(itemName.begin(), itemName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800685 itemName.end(), illegalDbusMemberRegex, "_");
686 std::string ifacePath = boardName;
687 ifacePath += "/";
688 ifacePath += itemName;
James Feistc6248a52018-08-14 10:09:45 -0700689
James Feistd58879a2019-09-11 11:26:07 -0700690 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
Ed Tanous07d467b2021-02-23 14:48:37 -0800691 createInterface(objServer, ifacePath,
James Feistd58879a2019-09-11 11:26:07 -0700692 "xyz.openbmc_project.Configuration." + itemType,
693 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -0800694
James Feist97a63f12018-05-17 13:50:57 -0700695 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700696 itemIface, item, objServer,
697 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -0800698
James Feista465ccc2019-02-08 12:51:01 -0800699 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800700 {
James Feist97a63f12018-05-17 13:50:57 -0700701 jsonPointerPath = jsonPointerPathBoard +
702 std::to_string(exposesIndex) + "/" +
703 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -0800704 if (objectPair.value().type() ==
705 nlohmann::json::value_t::object)
706 {
James Feistd58879a2019-09-11 11:26:07 -0700707 std::shared_ptr<sdbusplus::asio::dbus_interface>
708 objectIface = createInterface(
Ed Tanous07d467b2021-02-23 14:48:37 -0800709 objServer, ifacePath,
James Feistd58879a2019-09-11 11:26:07 -0700710 "xyz.openbmc_project.Configuration." + itemType +
711 "." + objectPair.key(),
712 boardKeyOrig);
James Feist97a63f12018-05-17 13:50:57 -0700713
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +0200714 populateInterfaceFromJson(systemConfiguration,
715 jsonPointerPath, objectIface,
716 objectPair.value(), objServer,
717 getPermission(objectPair.key()));
James Feist1b2e2242018-01-30 13:45:19 -0800718 }
719 else if (objectPair.value().type() ==
720 nlohmann::json::value_t::array)
721 {
722 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -0700723 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -0800724 {
James Feist8f2710a2018-05-09 17:18:55 -0700725 continue;
726 }
727 bool isLegal = true;
728 auto type = objectPair.value()[0].type();
729 if (type != nlohmann::json::value_t::object)
730 {
731 continue;
732 }
733
734 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -0800735 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700736 {
737 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800738 {
James Feist8f2710a2018-05-09 17:18:55 -0700739 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800740 break;
741 }
James Feist8f2710a2018-05-09 17:18:55 -0700742 }
743 if (!isLegal)
744 {
745 std::cerr << "dbus format error" << objectPair.value()
746 << "\n";
747 break;
748 }
749
James Feista465ccc2019-02-08 12:51:01 -0800750 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700751 {
James Feist97a63f12018-05-17 13:50:57 -0700752
James Feistd58879a2019-09-11 11:26:07 -0700753 std::shared_ptr<sdbusplus::asio::dbus_interface>
754 objectIface = createInterface(
Ed Tanous07d467b2021-02-23 14:48:37 -0800755 objServer, ifacePath,
James Feistd58879a2019-09-11 11:26:07 -0700756 "xyz.openbmc_project.Configuration." +
757 itemType + "." + objectPair.key() +
758 std::to_string(index),
759 boardKeyOrig);
760
James Feistc6248a52018-08-14 10:09:45 -0700761 populateInterfaceFromJson(
762 systemConfiguration,
763 jsonPointerPath + "/" + std::to_string(index),
764 objectIface, arrayItem, objServer,
765 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -0700766 index++;
James Feist1b2e2242018-01-30 13:45:19 -0800767 }
768 }
769 }
770 }
771 }
772}
773
James Feist8f2710a2018-05-09 17:18:55 -0700774// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -0800775bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -0800776{
777 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -0800778 std::vector<std::filesystem::path> jsonPaths;
Andrew Jefferya9c58922021-06-01 09:28:59 +0930779 if (!findFiles(
780 std::vector<std::filesystem::path>{configurationDirectory,
781 hostConfigurationDirectory},
782 R"(.*\.json)", jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -0800783 {
784 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -0700785 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -0800786 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800787 }
James Feistb4383f42018-08-06 16:54:10 -0700788
789 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
790 globalSchema);
791 if (!schemaStream.good())
792 {
793 std::cerr
794 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
795 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -0800796 return false;
James Feistb4383f42018-08-06 16:54:10 -0700797 }
798 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
799 if (schema.is_discarded())
800 {
801 std::cerr
802 << "Illegal schema file detected, cannot validate JSON, exiting\n";
803 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -0800804 return false;
James Feistb4383f42018-08-06 16:54:10 -0700805 }
806
James Feista465ccc2019-02-08 12:51:01 -0800807 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -0800808 {
809 std::ifstream jsonStream(jsonPath.c_str());
810 if (!jsonStream.good())
811 {
812 std::cerr << "unable to open " << jsonPath.string() << "\n";
813 continue;
814 }
815 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
816 if (data.is_discarded())
817 {
818 std::cerr << "syntax error in " << jsonPath.string() << "\n";
819 continue;
820 }
James Feist8da99192019-01-24 08:20:16 -0800821 /*
822 * todo(james): reenable this once less things are in flight
823 *
James Feistb4383f42018-08-06 16:54:10 -0700824 if (!validateJson(schema, data))
825 {
826 std::cerr << "Error validating " << jsonPath.string() << "\n";
827 continue;
828 }
James Feist8da99192019-01-24 08:20:16 -0800829 */
James Feistb4383f42018-08-06 16:54:10 -0700830
James Feist3cb5fec2018-01-23 14:41:51 -0800831 if (data.type() == nlohmann::json::value_t::array)
832 {
James Feista465ccc2019-02-08 12:51:01 -0800833 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -0800834 {
835 configurations.emplace_back(d);
836 }
837 }
838 else
839 {
840 configurations.emplace_back(data);
841 }
842 }
Ed Tanous072e25d2018-12-16 21:45:20 -0800843 return true;
James Feist75fdeeb2018-02-20 14:26:16 -0800844}
James Feist3cb5fec2018-01-23 14:41:51 -0800845
James Feistb1728ca2020-04-30 15:40:55 -0700846void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -0700847 nlohmann::json& systemConfiguration)
848{
849 static bool scannedPowerOff = false;
850 static bool scannedPowerOn = false;
851
James Feistfb00f392019-06-25 14:16:48 -0700852 if (systemConfiguration.empty() || lastJson.empty())
853 {
854 return; // not ready yet
855 }
James Feist1df06a42019-04-11 14:23:04 -0700856 if (scannedPowerOn)
857 {
858 return;
859 }
860
861 if (!isPowerOn() && scannedPowerOff)
862 {
863 return;
864 }
865
James Feistb1728ca2020-04-30 15:40:55 -0700866 timer.expires_after(std::chrono::seconds(10));
James Feist1a996582019-05-14 15:10:06 -0700867 timer.async_wait(
868 [&systemConfiguration](const boost::system::error_code& ec) {
869 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -0700870 {
James Feist1a996582019-05-14 15:10:06 -0700871 // we were cancelled
872 return;
873 }
874
875 bool powerOff = !isPowerOn();
876 for (const auto& item : lastJson.items())
877 {
878 if (systemConfiguration.find(item.key()) ==
879 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -0700880 {
James Feist1a996582019-05-14 15:10:06 -0700881 bool isDetectedPowerOn = false;
882 auto powerState = item.value().find("PowerState");
883 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -0700884 {
James Feist1a996582019-05-14 15:10:06 -0700885 auto ptr = powerState->get_ptr<const std::string*>();
886 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -0700887 {
James Feist1a996582019-05-14 15:10:06 -0700888 if (*ptr == "On" || *ptr == "BiosPost")
889 {
890 isDetectedPowerOn = true;
891 }
James Feist1df06a42019-04-11 14:23:04 -0700892 }
893 }
James Feist1a996582019-05-14 15:10:06 -0700894 if (powerOff && isDetectedPowerOn)
895 {
896 // power not on yet, don't know if it's there or not
897 continue;
898 }
899 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
900 {
901 // already logged it when power was off
902 continue;
903 }
James Feist1df06a42019-04-11 14:23:04 -0700904
James Feist1a996582019-05-14 15:10:06 -0700905 logDeviceRemoved(item.value());
906 }
James Feist1df06a42019-04-11 14:23:04 -0700907 }
James Feist1a996582019-05-14 15:10:06 -0700908 scannedPowerOff = true;
909 if (!powerOff)
910 {
911 scannedPowerOn = true;
912 }
913 });
James Feist1df06a42019-04-11 14:23:04 -0700914}
915
James Feist8f2710a2018-05-09 17:18:55 -0700916// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -0700917void propertiesChangedCallback(nlohmann::json& systemConfiguration,
918 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -0700919{
James Feist2539ccd2020-05-01 16:15:08 -0700920 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -0700921 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -0700922 static size_t instance = 0;
923 instance++;
924 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -0700925
James Feistb1728ca2020-04-30 15:40:55 -0700926 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -0700927
928 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -0700929 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -0700930 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -0700931 if (ec == boost::asio::error::operation_aborted)
932 {
933 // we were cancelled
934 return;
935 }
Ed Tanous07d467b2021-02-23 14:48:37 -0800936 if (ec)
James Feist8f2710a2018-05-09 17:18:55 -0700937 {
938 std::cerr << "async wait error " << ec << "\n";
939 return;
940 }
941
James Feist2539ccd2020-05-01 16:15:08 -0700942 if (inProgress)
943 {
944 propertiesChangedCallback(systemConfiguration, objServer);
945 return;
946 }
947 inProgress = true;
948
James Feist8f2710a2018-05-09 17:18:55 -0700949 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -0700950 auto missingConfigurations = std::make_shared<nlohmann::json>();
951 *missingConfigurations = systemConfiguration;
952
James Feist8f2710a2018-05-09 17:18:55 -0700953 std::list<nlohmann::json> configurations;
954 if (!findJsonFiles(configurations))
955 {
956 std::cerr << "cannot find json files\n";
James Feist2539ccd2020-05-01 16:15:08 -0700957 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -0700958 return;
959 }
960
961 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -0700962 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -0700963 objServer,
964 [&systemConfiguration, &objServer, count, oldConfiguration,
965 missingConfigurations]() {
James Feist899e17f2019-09-13 11:46:29 -0700966 // this is something that since ac has been applied to the bmc
967 // we saw, and we no longer see it
968 bool powerOff = !isPowerOn();
969 for (const auto& item : missingConfigurations->items())
970 {
971 bool isDetectedPowerOn = false;
972 auto powerState = item.value().find("PowerState");
973 if (powerState != item.value().end())
974 {
975 auto ptr = powerState->get_ptr<const std::string*>();
976 if (ptr)
977 {
978 if (*ptr == "On" || *ptr == "BiosPost")
979 {
980 isDetectedPowerOn = true;
981 }
982 }
983 }
984 if (powerOff && isDetectedPowerOn)
985 {
986 // power not on yet, don't know if it's there or not
987 continue;
988 }
989 std::string name = item.value()["Name"].get<std::string>();
James Feist02d2b932020-02-06 16:28:48 -0800990 std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
James Feist899e17f2019-09-13 11:46:29 -0700991 ifaces = inventory[name];
992 for (auto& iface : ifaces)
993 {
James Feist02d2b932020-02-06 16:28:48 -0800994 auto sharedPtr = iface.lock();
995 if (!sharedPtr)
996 {
997 continue; // was already deleted elsewhere
998 }
999 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -07001000 }
1001 ifaces.clear();
1002 systemConfiguration.erase(item.key());
1003 logDeviceRemoved(item.value());
1004 }
1005
James Feist8f2710a2018-05-09 17:18:55 -07001006 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001007 for (auto it = newConfiguration.begin();
1008 it != newConfiguration.end();)
1009 {
1010 auto findKey = oldConfiguration.find(it.key());
1011 if (findKey != oldConfiguration.end())
1012 {
1013 it = newConfiguration.erase(it);
1014 }
1015 else
1016 {
1017 it++;
1018 }
1019 }
James Feist899e17f2019-09-13 11:46:29 -07001020 for (const auto& item : newConfiguration.items())
1021 {
1022 logDeviceAdded(item.value());
1023 }
1024
James Feist2539ccd2020-05-01 16:15:08 -07001025 inProgress = false;
1026
Jonathan Doman6d649822021-05-05 16:53:04 -07001027 io.post([count, newConfiguration, &systemConfiguration,
1028 &objServer]() {
James Feist8f2710a2018-05-09 17:18:55 -07001029 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001030
Jonathan Doman6d649822021-05-05 16:53:04 -07001031 io.post([&systemConfiguration]() {
James Feistbb43d022018-06-12 15:44:33 -07001032 if (!writeJsonFiles(systemConfiguration))
1033 {
1034 std::cerr << "Error writing json files\n";
1035 }
1036 });
Jonathan Doman6d649822021-05-05 16:53:04 -07001037 io.post([count, newConfiguration, &systemConfiguration,
1038 &objServer]() {
James Feist97a63f12018-05-17 13:50:57 -07001039 postToDbus(newConfiguration, systemConfiguration,
1040 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001041 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001042 {
James Feist899e17f2019-09-13 11:46:29 -07001043 return;
James Feist1df06a42019-04-11 14:23:04 -07001044 }
James Feist899e17f2019-09-13 11:46:29 -07001045 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001046 });
1047 });
1048 });
1049 perfScan->run();
1050 });
James Feist75fdeeb2018-02-20 14:26:16 -08001051}
1052
James Feist98132792019-07-09 13:29:09 -07001053int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001054{
1055 // setup connection to dbus
Ed Tanous07d467b2021-02-23 14:48:37 -08001056 systemBus = std::make_shared<sdbusplus::asio::connection>(io);
1057 systemBus->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001058
Ed Tanous07d467b2021-02-23 14:48:37 -08001059 sdbusplus::asio::object_server objServer(systemBus);
James Feistfd1264a2018-05-03 12:10:00 -07001060
James Feist8f2710a2018-05-09 17:18:55 -07001061 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1062 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1063 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001064
James Feist4131aea2018-03-09 09:47:30 -08001065 // to keep reference to the match / filter objects so they don't get
1066 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001067
1068 nlohmann::json systemConfiguration = nlohmann::json::object();
1069
Brad Bishopc76af0f2020-12-04 13:50:23 -05001070 // We need a poke from DBus for static providers that create all their
1071 // objects prior to claiming a well-known name, and thus don't emit any
1072 // org.freedesktop.DBus.Properties signals. Similarly if a process exits
1073 // for any reason, expected or otherwise, we'll need a poke to remove
1074 // entities from DBus.
1075 sdbusplus::bus::match::match nameOwnerChangedMatch(
Ed Tanous07d467b2021-02-23 14:48:37 -08001076 static_cast<sdbusplus::bus::bus&>(*systemBus),
Brad Bishopc76af0f2020-12-04 13:50:23 -05001077 sdbusplus::bus::match::rules::nameOwnerChanged(),
1078 [&](sdbusplus::message::message&) {
1079 propertiesChangedCallback(systemConfiguration, objServer);
1080 });
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001081 // We also need a poke from DBus when new interfaces are created or
1082 // destroyed.
1083 sdbusplus::bus::match::match interfacesAddedMatch(
Ed Tanous07d467b2021-02-23 14:48:37 -08001084 static_cast<sdbusplus::bus::bus&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001085 sdbusplus::bus::match::rules::interfacesAdded(),
1086 [&](sdbusplus::message::message&) {
1087 propertiesChangedCallback(systemConfiguration, objServer);
1088 });
1089 sdbusplus::bus::match::match interfacesRemovedMatch(
Ed Tanous07d467b2021-02-23 14:48:37 -08001090 static_cast<sdbusplus::bus::bus&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001091 sdbusplus::bus::match::rules::interfacesRemoved(),
1092 [&](sdbusplus::message::message&) {
1093 propertiesChangedCallback(systemConfiguration, objServer);
1094 });
Brad Bishopc76af0f2020-12-04 13:50:23 -05001095
James Feist4dc617b2020-05-01 09:54:47 -07001096 io.post(
1097 [&]() { propertiesChangedCallback(systemConfiguration, objServer); });
James Feist4131aea2018-03-09 09:47:30 -08001098
James Feistfd1264a2018-05-03 12:10:00 -07001099 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001100 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001101 });
James Feist8f2710a2018-05-09 17:18:55 -07001102 entityIface->initialize();
1103
James Feist1df06a42019-04-11 14:23:04 -07001104 if (fwVersionIsSame())
1105 {
1106 if (std::filesystem::is_regular_file(currentConfiguration))
1107 {
1108 // this file could just be deleted, but it's nice for debug
1109 std::filesystem::create_directory(tempConfigDir);
1110 std::filesystem::remove(lastConfiguration);
1111 std::filesystem::copy(currentConfiguration, lastConfiguration);
1112 std::filesystem::remove(currentConfiguration);
1113
1114 std::ifstream jsonStream(lastConfiguration);
1115 if (jsonStream.good())
1116 {
1117 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1118 if (data.is_discarded())
1119 {
1120 std::cerr << "syntax error in " << lastConfiguration
1121 << "\n";
1122 }
1123 else
1124 {
1125 lastJson = std::move(data);
1126 }
1127 }
1128 else
1129 {
1130 std::cerr << "unable to open " << lastConfiguration << "\n";
1131 }
1132 }
1133 }
1134 else
1135 {
1136 // not an error, just logging at this level to make it in the journal
1137 std::cerr << "Clearing previous configuration\n";
1138 std::filesystem::remove(currentConfiguration);
1139 }
1140
1141 // some boards only show up after power is on, we want to not say they are
1142 // removed until the same state happens
Ed Tanous07d467b2021-02-23 14:48:37 -08001143 setupPowerMatch(systemBus);
James Feist1df06a42019-04-11 14:23:04 -07001144
James Feist1b2e2242018-01-30 13:45:19 -08001145 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001146
1147 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001148}