blob: 3b89e961c92a5095b078112764627ba85deb4f5e [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";
James Feistf1b14142019-04-10 15:22:09 -070052
Andrew Jeffery666583b2021-12-01 15:50:12 +103053const boost::container::flat_map<const char*, probe_type_codes, CmpStr>
Ed Tanous07d467b2021-02-23 14:48:37 -080054 probeTypes{{{"FALSE", probe_type_codes::FALSE_T},
55 {"TRUE", probe_type_codes::TRUE_T},
56 {"AND", probe_type_codes::AND},
57 {"OR", probe_type_codes::OR},
58 {"FOUND", probe_type_codes::FOUND},
59 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080060
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020061static constexpr std::array<const char*, 6> settableInterfaces = {
62 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist68500ff2018-08-08 15:40:42 -070063using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080064 std::variant<std::vector<std::string>, std::vector<double>, std::string,
65 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
66 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080067
68using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070069 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080070 boost::container::flat_map<
71 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070072 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080073
James Feistd58879a2019-09-11 11:26:07 -070074// store reference to all interfaces so we can destroy them later
75boost::container::flat_map<
James Feist02d2b932020-02-06 16:28:48 -080076 std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
James Feistd58879a2019-09-11 11:26:07 -070077 inventory;
78
James Feist3cb5fec2018-01-23 14:41:51 -080079// todo: pass this through nicer
Ed Tanous07d467b2021-02-23 14:48:37 -080080std::shared_ptr<sdbusplus::asio::connection> systemBus;
Andrew Jeffery47af65a2021-12-01 14:16:31 +103081nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -080082
James Feist02d2b932020-02-06 16:28:48 -080083boost::asio::io_context io;
84
Ed Tanous07d467b2021-02-23 14:48:37 -080085const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
86const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -080087
Andrew Jeffery666583b2021-12-01 15:50:12 +103088FoundProbeTypeT findProbeType(const std::string& probe)
89{
90 boost::container::flat_map<const char*, probe_type_codes,
91 CmpStr>::const_iterator probeType;
92 for (probeType = probeTypes.begin(); probeType != probeTypes.end();
93 ++probeType)
94 {
95 if (probe.find(probeType->first) != std::string::npos)
96 {
97 return probeType;
98 }
99 }
100
101 return std::nullopt;
102}
103
James Feistd58879a2019-09-11 11:26:07 -0700104static std::shared_ptr<sdbusplus::asio::dbus_interface>
105 createInterface(sdbusplus::asio::object_server& objServer,
106 const std::string& path, const std::string& interface,
James Feist02d2b932020-02-06 16:28:48 -0800107 const std::string& parent, bool checkNull = false)
James Feistd58879a2019-09-11 11:26:07 -0700108{
James Feist02d2b932020-02-06 16:28:48 -0800109 // on first add we have no reason to check for null before add, as there
110 // won't be any. For dynamically added interfaces, we check for null so that
111 // a constant delete/add will not create a memory leak
112
113 auto ptr = objServer.add_interface(path, interface);
114 auto& dataVector = inventory[parent];
115 if (checkNull)
116 {
117 auto it = std::find_if(dataVector.begin(), dataVector.end(),
118 [](const auto& p) { return p.expired(); });
119 if (it != dataVector.end())
120 {
121 *it = ptr;
122 return ptr;
123 }
124 }
125 dataVector.emplace_back(ptr);
126 return ptr;
James Feistd58879a2019-09-11 11:26:07 -0700127}
128
James Feist8f2710a2018-05-09 17:18:55 -0700129// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800130bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800131{
James Feist1df06a42019-04-11 14:23:04 -0700132 std::filesystem::create_directory(configurationOutDir);
133 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700134 if (!output.good())
135 {
136 return false;
137 }
James Feist1b2e2242018-01-30 13:45:19 -0800138 output << systemConfiguration.dump(4);
139 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700140 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700141}
James Feist1b2e2242018-01-30 13:45:19 -0800142
James Feist97a63f12018-05-17 13:50:57 -0700143template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800144bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
145 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700146{
147 try
148 {
149 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800150 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700151 ref = value;
152 return true;
153 }
James Feist98132792019-07-09 13:29:09 -0700154 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700155 {
156 return false;
157 }
158}
James Feistbb43d022018-06-12 15:44:33 -0700159
James Feistebcc26b2019-03-22 12:30:43 -0700160// template function to add array as dbus property
161template <typename PropertyType>
162void addArrayToDbus(const std::string& name, const nlohmann::json& array,
163 sdbusplus::asio::dbus_interface* iface,
164 sdbusplus::asio::PropertyPermission permission,
165 nlohmann::json& systemConfiguration,
166 const std::string& jsonPointerString)
167{
168 std::vector<PropertyType> values;
169 for (const auto& property : array)
170 {
171 auto ptr = property.get_ptr<const PropertyType*>();
172 if (ptr != nullptr)
173 {
174 values.emplace_back(*ptr);
175 }
176 }
177
178 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
179 {
180 iface->register_property(name, values);
181 }
182 else
183 {
184 iface->register_property(
185 name, values,
186 [&systemConfiguration,
187 jsonPointerString{std::string(jsonPointerString)}](
188 const std::vector<PropertyType>& newVal,
189 std::vector<PropertyType>& val) {
190 val = newVal;
191 if (!setJsonFromPointer(jsonPointerString, val,
192 systemConfiguration))
193 {
194 std::cerr << "error setting json field\n";
195 return -1;
196 }
197 if (!writeJsonFiles(systemConfiguration))
198 {
199 std::cerr << "error setting json file\n";
200 return -1;
201 }
202 return 1;
203 });
204 }
205}
206
James Feistbb43d022018-06-12 15:44:33 -0700207template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800208void addProperty(const std::string& propertyName, const PropertyType& value,
209 sdbusplus::asio::dbus_interface* iface,
210 nlohmann::json& systemConfiguration,
211 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700212 sdbusplus::asio::PropertyPermission permission)
213{
214 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
215 {
216 iface->register_property(propertyName, value);
217 return;
218 }
James Feist68500ff2018-08-08 15:40:42 -0700219 iface->register_property(
220 propertyName, value,
221 [&systemConfiguration,
222 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800223 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700224 val = newVal;
225 if (!setJsonFromPointer(jsonPointerString, val,
226 systemConfiguration))
227 {
228 std::cerr << "error setting json field\n";
229 return -1;
230 }
James Feistc6248a52018-08-14 10:09:45 -0700231 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700232 {
233 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700234 return -1;
235 }
236 return 1;
237 });
238}
239
240void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800241 const std::string& jsonPointerPath,
242 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
243 sdbusplus::asio::object_server& objServer,
244 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700245{
246 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
247 iface->register_method(
248 "Delete", [&objServer, &systemConfiguration, interface,
249 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700250 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700251 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700252 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700253 {
254 // this technically can't happen as the pointer is pointing to
255 // us
256 throw DBusInternalError();
257 }
258 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feistc6248a52018-08-14 10:09:45 -0700259 systemConfiguration[ptr] = nullptr;
260
James Feist02d2b932020-02-06 16:28:48 -0800261 // todo(james): dig through sdbusplus to find out why we can't
262 // delete it in a method call
263 io.post([&objServer, dbusInterface]() mutable {
264 objServer.remove_interface(dbusInterface);
265 });
266
James Feistc6248a52018-08-14 10:09:45 -0700267 if (!writeJsonFiles(systemConfiguration))
268 {
269 std::cerr << "error setting json file\n";
270 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700271 }
James Feist68500ff2018-08-08 15:40:42 -0700272 });
James Feistbb43d022018-06-12 15:44:33 -0700273}
274
James Feist1b2e2242018-01-30 13:45:19 -0800275// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700276void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800277 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
278 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
279 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700280 sdbusplus::asio::PropertyPermission permission =
281 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800282{
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030283 for (auto& [key, value] : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800284 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030285 auto type = value.type();
James Feist8f2710a2018-05-09 17:18:55 -0700286 bool array = false;
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030287 if (value.type() == nlohmann::json::value_t::array)
James Feist8f2710a2018-05-09 17:18:55 -0700288 {
289 array = true;
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030290 if (!value.size())
James Feist8f2710a2018-05-09 17:18:55 -0700291 {
292 continue;
293 }
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030294 type = value[0].type();
James Feist8f2710a2018-05-09 17:18:55 -0700295 bool isLegal = true;
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030296 for (const auto& arrayItem : value)
James Feist8f2710a2018-05-09 17:18:55 -0700297 {
298 if (arrayItem.type() != type)
299 {
300 isLegal = false;
301 break;
302 }
303 }
304 if (!isLegal)
305 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030306 std::cerr << "dbus format error" << value << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700307 continue;
308 }
James Feista218ddb2019-04-11 14:01:31 -0700309 }
310 if (type == nlohmann::json::value_t::object)
311 {
312 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700313 }
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030314
315 std::string path = jsonPointerPath;
316 path.append("/").append(key);
James Feistbb43d022018-06-12 15:44:33 -0700317 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
318 {
319 // all setable numbers are doubles as it is difficult to always
320 // create a configuration file with all whole numbers as decimals
321 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700322 if (array)
323 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030324 if (value[0].is_number())
James Feistebcc26b2019-03-22 12:30:43 -0700325 {
326 type = nlohmann::json::value_t::number_float;
327 }
328 }
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030329 else if (value.is_number())
James Feistbb43d022018-06-12 15:44:33 -0700330 {
331 type = nlohmann::json::value_t::number_float;
332 }
333 }
334
James Feist8f2710a2018-05-09 17:18:55 -0700335 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800336 {
James Feist9eb0b582018-04-27 12:15:46 -0700337 case (nlohmann::json::value_t::boolean):
338 {
James Feist8f2710a2018-05-09 17:18:55 -0700339 if (array)
340 {
341 // todo: array of bool isn't detected correctly by
342 // sdbusplus, change it to numbers
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030343 addArrayToDbus<uint64_t>(key, value, iface.get(),
344 permission, systemConfiguration,
345 path);
James Feist8f2710a2018-05-09 17:18:55 -0700346 }
James Feistbb43d022018-06-12 15:44:33 -0700347
James Feist97a63f12018-05-17 13:50:57 -0700348 else
349 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030350 addProperty(key, value.get<bool>(), iface.get(),
351 systemConfiguration, path, 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 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030359 addArrayToDbus<int64_t>(key, value, iface.get(), permission,
Andrew Jeffery029ee282022-03-25 13:11:36 +1030360 systemConfiguration, path);
James Feist97a63f12018-05-17 13:50:57 -0700361 }
362 else
363 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030364 addProperty(key, value.get<int64_t>(), iface.get(),
365 systemConfiguration, path,
James Feistbb43d022018-06-12 15:44:33 -0700366 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_unsigned):
371 {
James Feist8f2710a2018-05-09 17:18:55 -0700372 if (array)
373 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030374 addArrayToDbus<uint64_t>(key, value, iface.get(),
375 permission, systemConfiguration,
376 path);
James Feist97a63f12018-05-17 13:50:57 -0700377 }
378 else
379 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030380 addProperty(key, value.get<uint64_t>(), iface.get(),
Andrew Jeffery029ee282022-03-25 13:11:36 +1030381 systemConfiguration, path,
James Feistbb43d022018-06-12 15:44:33 -0700382 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700383 }
James Feist9eb0b582018-04-27 12:15:46 -0700384 break;
385 }
386 case (nlohmann::json::value_t::number_float):
387 {
James Feist8f2710a2018-05-09 17:18:55 -0700388 if (array)
389 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030390 addArrayToDbus<double>(key, value, iface.get(), permission,
Andrew Jeffery029ee282022-03-25 13:11:36 +1030391 systemConfiguration, path);
James Feist8f2710a2018-05-09 17:18:55 -0700392 }
James Feistbb43d022018-06-12 15:44:33 -0700393
James Feist97a63f12018-05-17 13:50:57 -0700394 else
395 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030396 addProperty(key, value.get<double>(), iface.get(),
397 systemConfiguration, path, permission);
James Feist97a63f12018-05-17 13:50:57 -0700398 }
James Feist9eb0b582018-04-27 12:15:46 -0700399 break;
400 }
401 case (nlohmann::json::value_t::string):
402 {
James Feist8f2710a2018-05-09 17:18:55 -0700403 if (array)
404 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030405 addArrayToDbus<std::string>(key, value, iface.get(),
406 permission, systemConfiguration,
407 path);
James Feist97a63f12018-05-17 13:50:57 -0700408 }
409 else
410 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030411 addProperty(key, value.get<std::string>(), iface.get(),
412 systemConfiguration, path, permission);
James Feist97a63f12018-05-17 13:50:57 -0700413 }
James Feist9eb0b582018-04-27 12:15:46 -0700414 break;
415 }
James Feist0eb40352019-04-09 14:44:04 -0700416 default:
417 {
James Feista218ddb2019-04-11 14:01:31 -0700418 std::cerr << "Unexpected json type in system configuration "
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030419 << key << ": " << value.type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700420 break;
421 }
James Feist1b2e2242018-01-30 13:45:19 -0800422 }
423 }
James Feistc6248a52018-08-14 10:09:45 -0700424 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
425 {
426 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
427 systemConfiguration);
428 }
James Feist8f2710a2018-05-09 17:18:55 -0700429 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800430}
431
James Feista465ccc2019-02-08 12:51:01 -0800432sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700433{
434 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
435 interface) != settableInterfaces.end()
436 ? sdbusplus::asio::PropertyPermission::readWrite
437 : sdbusplus::asio::PropertyPermission::readOnly;
438}
439
James Feista465ccc2019-02-08 12:51:01 -0800440void createAddObjectMethod(const std::string& jsonPointerPath,
441 const std::string& path,
442 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700443 sdbusplus::asio::object_server& objServer,
444 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700445{
James Feistd58879a2019-09-11 11:26:07 -0700446 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
447 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700448
449 iface->register_method(
450 "AddObject",
451 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700452 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
453 board](const boost::container::flat_map<std::string, JsonVariantType>&
454 data) {
James Feist68500ff2018-08-08 15:40:42 -0700455 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800456 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700457 auto findExposes = base.find("Exposes");
458
459 if (findExposes == base.end())
460 {
461 throw std::invalid_argument("Entity must have children.");
462 }
463
464 // this will throw invalid-argument to sdbusplus if invalid json
465 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800466 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700467 {
James Feista465ccc2019-02-08 12:51:01 -0800468 nlohmann::json& newJson = newData[item.first];
469 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
470 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700471 }
472
473 auto findName = newData.find("Name");
474 auto findType = newData.find("Type");
475 if (findName == newData.end() || findType == newData.end())
476 {
477 throw std::invalid_argument("AddObject missing Name or Type");
478 }
James Feista465ccc2019-02-08 12:51:01 -0800479 const std::string* type = findType->get_ptr<const std::string*>();
480 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700481 if (type == nullptr || name == nullptr)
482 {
483 throw std::invalid_argument("Type and Name must be a string.");
484 }
485
James Feist02d2b932020-02-06 16:28:48 -0800486 bool foundNull = false;
James Feist68500ff2018-08-08 15:40:42 -0700487 size_t lastIndex = 0;
488 // we add in the "exposes"
James Feist02d2b932020-02-06 16:28:48 -0800489 for (const auto& expose : *findExposes)
James Feist68500ff2018-08-08 15:40:42 -0700490 {
James Feist02d2b932020-02-06 16:28:48 -0800491 if (expose.is_null())
492 {
493 foundNull = true;
494 continue;
495 }
496
497 if (expose["Name"] == *name && expose["Type"] == *type)
James Feist68500ff2018-08-08 15:40:42 -0700498 {
499 throw std::invalid_argument(
500 "Field already in JSON, not adding");
501 }
James Feist02d2b932020-02-06 16:28:48 -0800502
503 if (foundNull)
504 {
505 continue;
506 }
507
James Feist68500ff2018-08-08 15:40:42 -0700508 lastIndex++;
509 }
510
511 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
512 *type + ".json");
513 // todo(james) we might want to also make a list of 'can add'
514 // interfaces but for now I think the assumption if there is a
515 // schema avaliable that it is allowed to update is fine
516 if (!schemaFile.good())
517 {
518 throw std::invalid_argument(
519 "No schema avaliable, cannot validate.");
520 }
521 nlohmann::json schema =
522 nlohmann::json::parse(schemaFile, nullptr, false);
523 if (schema.is_discarded())
524 {
525 std::cerr << "Schema not legal" << *type << ".json\n";
526 throw DBusInternalError();
527 }
528 if (!validateJson(schema, newData))
529 {
530 throw std::invalid_argument("Data does not match schema");
531 }
James Feist02d2b932020-02-06 16:28:48 -0800532 if (foundNull)
533 {
534 findExposes->at(lastIndex) = newData;
535 }
536 else
537 {
538 findExposes->push_back(newData);
539 }
James Feist68500ff2018-08-08 15:40:42 -0700540 if (!writeJsonFiles(systemConfiguration))
541 {
542 std::cerr << "Error writing json files\n";
543 throw DBusInternalError();
544 }
545 std::string dbusName = *name;
546
547 std::regex_replace(dbusName.begin(), dbusName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800548 dbusName.end(), illegalDbusMemberRegex, "_");
James Feistd58879a2019-09-11 11:26:07 -0700549
550 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
551 createInterface(objServer, path + "/" + dbusName,
552 "xyz.openbmc_project.Configuration." + *type,
James Feist02d2b932020-02-06 16:28:48 -0800553 board, true);
James Feist68500ff2018-08-08 15:40:42 -0700554 // permission is read-write, as since we just created it, must be
555 // runtime modifiable
556 populateInterfaceFromJson(
557 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700558 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700559 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700560 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700561 });
562 iface->initialize();
563}
564
James Feista465ccc2019-02-08 12:51:01 -0800565void postToDbus(const nlohmann::json& newConfiguration,
566 nlohmann::json& systemConfiguration,
567 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800568
James Feist1b2e2242018-01-30 13:45:19 -0800569{
James Feist97a63f12018-05-17 13:50:57 -0700570 // iterate through boards
Andrew Jeffery13132df2022-03-25 13:29:41 +1030571 for (auto& [boardId, boardConfig] : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800572 {
Andrew Jeffery13132df2022-03-25 13:29:41 +1030573 std::string boardKey = boardConfig["Name"];
574 std::string boardKeyOrig = boardConfig["Name"];
575 std::string jsonPointerPath = "/" + boardId;
James Feist97a63f12018-05-17 13:50:57 -0700576 // loop through newConfiguration, but use values from system
577 // configuration to be able to modify via dbus later
Andrew Jeffery13132df2022-03-25 13:29:41 +1030578 auto boardValues = systemConfiguration[boardId];
James Feistd63d18a2018-07-19 15:23:45 -0700579 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800580 std::string boardType;
581 if (findBoardType != boardValues.end() &&
582 findBoardType->type() == nlohmann::json::value_t::string)
583 {
584 boardType = findBoardType->get<std::string>();
585 std::regex_replace(boardType.begin(), boardType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800586 boardType.end(), illegalDbusMemberRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800587 }
588 else
589 {
590 std::cerr << "Unable to find type for " << boardKey
591 << " reverting to Chassis.\n";
592 boardType = "Chassis";
593 }
James Feist11be6672018-04-06 14:05:32 -0700594 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800595
596 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800597 illegalDbusMemberRegex, "_");
598 std::string boardName = "/xyz/openbmc_project/inventory/system/";
599 boardName += boardtypeLower;
600 boardName += "/";
601 boardName += boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800602
James Feistd58879a2019-09-11 11:26:07 -0700603 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
604 createInterface(objServer, boardName,
605 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700606
James Feistd58879a2019-09-11 11:26:07 -0700607 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
608 createInterface(objServer, boardName,
609 "xyz.openbmc_project.Inventory.Item." + boardType,
610 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -0700611
James Feist68500ff2018-08-08 15:40:42 -0700612 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700613 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -0700614
James Feist97a63f12018-05-17 13:50:57 -0700615 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700616 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700617 jsonPointerPath += "/";
618 // iterate through board properties
Andrew Jefferya96950d2022-03-25 13:32:46 +1030619 for (auto& [propName, propValue] : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700620 {
Andrew Jefferya96950d2022-03-25 13:32:46 +1030621 if (propValue.type() == nlohmann::json::value_t::object)
James Feist11be6672018-04-06 14:05:32 -0700622 {
James Feistd58879a2019-09-11 11:26:07 -0700623 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
Andrew Jefferya96950d2022-03-25 13:32:46 +1030624 createInterface(objServer, boardName, propName,
James Feistd58879a2019-09-11 11:26:07 -0700625 boardKeyOrig);
626
James Feistc6248a52018-08-14 10:09:45 -0700627 populateInterfaceFromJson(systemConfiguration,
Andrew Jefferya96950d2022-03-25 13:32:46 +1030628 jsonPointerPath + propName, iface,
629 propValue, objServer);
James Feist11be6672018-04-06 14:05:32 -0700630 }
631 }
James Feist97a63f12018-05-17 13:50:57 -0700632
James Feist1e3e6982018-08-03 16:09:28 -0700633 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800634 if (exposes == boardValues.end())
635 {
636 continue;
637 }
James Feist97a63f12018-05-17 13:50:57 -0700638 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700639 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700640
641 // store the board level pointer so we can modify it on the way down
642 std::string jsonPointerPathBoard = jsonPointerPath;
643 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -0800644 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -0800645 {
James Feist97a63f12018-05-17 13:50:57 -0700646 exposesIndex++;
647 jsonPointerPath = jsonPointerPathBoard;
648 jsonPointerPath += std::to_string(exposesIndex);
649
James Feistd63d18a2018-07-19 15:23:45 -0700650 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800651 if (findName == item.end())
652 {
653 std::cerr << "cannot find name in field " << item << "\n";
654 continue;
655 }
James Feist1e3e6982018-08-03 16:09:28 -0700656 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800657 // if status is not found it is assumed to be status = 'okay'
658 if (findStatus != item.end())
659 {
660 if (*findStatus == "disabled")
661 {
662 continue;
663 }
664 }
James Feistd63d18a2018-07-19 15:23:45 -0700665 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800666 std::string itemType;
667 if (findType != item.end())
668 {
669 itemType = findType->get<std::string>();
670 std::regex_replace(itemType.begin(), itemType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800671 itemType.end(), illegalDbusPathRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800672 }
673 else
674 {
675 itemType = "unknown";
676 }
677 std::string itemName = findName->get<std::string>();
678 std::regex_replace(itemName.begin(), itemName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800679 itemName.end(), illegalDbusMemberRegex, "_");
680 std::string ifacePath = boardName;
681 ifacePath += "/";
682 ifacePath += itemName;
James Feistc6248a52018-08-14 10:09:45 -0700683
James Feistd58879a2019-09-11 11:26:07 -0700684 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
Ed Tanous07d467b2021-02-23 14:48:37 -0800685 createInterface(objServer, ifacePath,
James Feistd58879a2019-09-11 11:26:07 -0700686 "xyz.openbmc_project.Configuration." + itemType,
687 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -0800688
James Feist97a63f12018-05-17 13:50:57 -0700689 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700690 itemIface, item, objServer,
691 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -0800692
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030693 for (auto& [name, config] : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800694 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030695 jsonPointerPath = jsonPointerPathBoard;
696 jsonPointerPath.append(std::to_string(exposesIndex))
697 .append("/")
698 .append(name);
699 if (config.type() == nlohmann::json::value_t::object)
James Feist1b2e2242018-01-30 13:45:19 -0800700 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030701 std::string ifaceName =
702 "xyz.openbmc_project.Configuration.";
703 ifaceName.append(itemType).append(".").append(name);
James Feist97a63f12018-05-17 13:50:57 -0700704
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030705 std::shared_ptr<sdbusplus::asio::dbus_interface>
706 objectIface = createInterface(objServer, ifacePath,
707 ifaceName, boardKeyOrig);
708
709 populateInterfaceFromJson(
710 systemConfiguration, jsonPointerPath, objectIface,
711 config, objServer, getPermission(name));
James Feist1b2e2242018-01-30 13:45:19 -0800712 }
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030713 else if (config.type() == nlohmann::json::value_t::array)
James Feist1b2e2242018-01-30 13:45:19 -0800714 {
715 size_t index = 0;
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030716 if (!config.size())
James Feist1b2e2242018-01-30 13:45:19 -0800717 {
James Feist8f2710a2018-05-09 17:18:55 -0700718 continue;
719 }
720 bool isLegal = true;
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030721 auto type = config[0].type();
James Feist8f2710a2018-05-09 17:18:55 -0700722 if (type != nlohmann::json::value_t::object)
723 {
724 continue;
725 }
726
727 // verify legal json
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030728 for (const auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700729 {
730 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800731 {
James Feist8f2710a2018-05-09 17:18:55 -0700732 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800733 break;
734 }
James Feist8f2710a2018-05-09 17:18:55 -0700735 }
736 if (!isLegal)
737 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030738 std::cerr << "dbus format error" << config << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700739 break;
740 }
741
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030742 for (auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700743 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030744 std::string ifaceName =
745 "xyz.openbmc_project.Configuration.";
746 ifaceName.append(itemType).append(".").append(name);
747 ifaceName.append(std::to_string(index));
James Feist97a63f12018-05-17 13:50:57 -0700748
James Feistd58879a2019-09-11 11:26:07 -0700749 std::shared_ptr<sdbusplus::asio::dbus_interface>
750 objectIface = createInterface(
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030751 objServer, ifacePath, ifaceName, boardKeyOrig);
James Feistd58879a2019-09-11 11:26:07 -0700752
James Feistc6248a52018-08-14 10:09:45 -0700753 populateInterfaceFromJson(
754 systemConfiguration,
755 jsonPointerPath + "/" + std::to_string(index),
756 objectIface, arrayItem, objServer,
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030757 getPermission(name));
James Feistbb43d022018-06-12 15:44:33 -0700758 index++;
James Feist1b2e2242018-01-30 13:45:19 -0800759 }
760 }
761 }
762 }
763 }
764}
765
James Feist8f2710a2018-05-09 17:18:55 -0700766// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -0800767bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -0800768{
769 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -0800770 std::vector<std::filesystem::path> jsonPaths;
Andrew Jefferya9c58922021-06-01 09:28:59 +0930771 if (!findFiles(
772 std::vector<std::filesystem::path>{configurationDirectory,
773 hostConfigurationDirectory},
774 R"(.*\.json)", jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -0800775 {
776 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -0700777 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -0800778 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800779 }
James Feistb4383f42018-08-06 16:54:10 -0700780
781 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
782 globalSchema);
783 if (!schemaStream.good())
784 {
785 std::cerr
786 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
787 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -0800788 return false;
James Feistb4383f42018-08-06 16:54:10 -0700789 }
790 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
791 if (schema.is_discarded())
792 {
793 std::cerr
794 << "Illegal schema file detected, cannot validate JSON, exiting\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
James Feista465ccc2019-02-08 12:51:01 -0800799 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -0800800 {
801 std::ifstream jsonStream(jsonPath.c_str());
802 if (!jsonStream.good())
803 {
804 std::cerr << "unable to open " << jsonPath.string() << "\n";
805 continue;
806 }
807 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
808 if (data.is_discarded())
809 {
810 std::cerr << "syntax error in " << jsonPath.string() << "\n";
811 continue;
812 }
James Feist8da99192019-01-24 08:20:16 -0800813 /*
814 * todo(james): reenable this once less things are in flight
815 *
James Feistb4383f42018-08-06 16:54:10 -0700816 if (!validateJson(schema, data))
817 {
818 std::cerr << "Error validating " << jsonPath.string() << "\n";
819 continue;
820 }
James Feist8da99192019-01-24 08:20:16 -0800821 */
James Feistb4383f42018-08-06 16:54:10 -0700822
James Feist3cb5fec2018-01-23 14:41:51 -0800823 if (data.type() == nlohmann::json::value_t::array)
824 {
James Feista465ccc2019-02-08 12:51:01 -0800825 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -0800826 {
827 configurations.emplace_back(d);
828 }
829 }
830 else
831 {
832 configurations.emplace_back(data);
833 }
834 }
Ed Tanous072e25d2018-12-16 21:45:20 -0800835 return true;
James Feist75fdeeb2018-02-20 14:26:16 -0800836}
James Feist3cb5fec2018-01-23 14:41:51 -0800837
Andrew Jeffery55192932022-03-24 12:29:27 +1030838static bool deviceRequiresPowerOn(const nlohmann::json& entity)
839{
840 auto powerState = entity.find("PowerState");
Andrew Jefferyb6209442022-03-24 12:36:20 +1030841 if (powerState == entity.end())
Andrew Jeffery55192932022-03-24 12:29:27 +1030842 {
Andrew Jefferyb6209442022-03-24 12:36:20 +1030843 return false;
Andrew Jeffery55192932022-03-24 12:29:27 +1030844 }
845
Andrew Jefferyb6209442022-03-24 12:36:20 +1030846 auto ptr = powerState->get_ptr<const std::string*>();
847 if (!ptr)
848 {
849 return false;
850 }
851
852 return *ptr == "On" || *ptr == "BiosPost";
Andrew Jeffery55192932022-03-24 12:29:27 +1030853}
854
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030855static void pruneDevice(const nlohmann::json& systemConfiguration,
856 const bool powerOff, const bool scannedPowerOff,
857 const std::string& name, const nlohmann::json& device)
858{
859 if (systemConfiguration.contains(name))
860 {
861 return;
862 }
863
Andrew Jeffery4db38bc2022-03-24 13:42:41 +1030864 if (deviceRequiresPowerOn(device) && (powerOff || scannedPowerOff))
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030865 {
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030866 return;
867 }
868
869 logDeviceRemoved(device);
870}
871
James Feistb1728ca2020-04-30 15:40:55 -0700872void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -0700873 nlohmann::json& systemConfiguration)
874{
875 static bool scannedPowerOff = false;
876 static bool scannedPowerOn = false;
877
James Feistfb00f392019-06-25 14:16:48 -0700878 if (systemConfiguration.empty() || lastJson.empty())
879 {
880 return; // not ready yet
881 }
James Feist1df06a42019-04-11 14:23:04 -0700882 if (scannedPowerOn)
883 {
884 return;
885 }
886
887 if (!isPowerOn() && scannedPowerOff)
888 {
889 return;
890 }
891
James Feistb1728ca2020-04-30 15:40:55 -0700892 timer.expires_after(std::chrono::seconds(10));
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030893 timer.async_wait(
894 [&systemConfiguration](const boost::system::error_code& ec) {
895 if (ec == boost::asio::error::operation_aborted)
James Feist1a996582019-05-14 15:10:06 -0700896 {
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030897 return;
James Feist1df06a42019-04-11 14:23:04 -0700898 }
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030899
900 bool powerOff = !isPowerOn();
Andrew Jefferya82c55d2022-03-24 14:17:28 +1030901 for (const auto& [name, device] : lastJson.items())
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030902 {
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030903 pruneDevice(systemConfiguration, powerOff, scannedPowerOff,
904 name, device);
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030905 }
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030906
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030907 scannedPowerOff = true;
908 if (!powerOff)
909 {
910 scannedPowerOn = true;
911 }
912 });
James Feist1df06a42019-04-11 14:23:04 -0700913}
914
Andrew Jeffery2f750d22022-03-24 14:32:57 +1030915static std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
916 getDeviceInterfaces(const nlohmann::json& device)
917{
918 return inventory[device["Name"].get<std::string>()];
919}
920
James Feist8f2710a2018-05-09 17:18:55 -0700921// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -0700922void propertiesChangedCallback(nlohmann::json& systemConfiguration,
923 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -0700924{
James Feist2539ccd2020-05-01 16:15:08 -0700925 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -0700926 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -0700927 static size_t instance = 0;
928 instance++;
929 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -0700930
James Feistb1728ca2020-04-30 15:40:55 -0700931 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -0700932
933 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -0700934 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -0700935 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -0700936 if (ec == boost::asio::error::operation_aborted)
937 {
938 // we were cancelled
939 return;
940 }
Ed Tanous07d467b2021-02-23 14:48:37 -0800941 if (ec)
James Feist8f2710a2018-05-09 17:18:55 -0700942 {
943 std::cerr << "async wait error " << ec << "\n";
944 return;
945 }
946
James Feist2539ccd2020-05-01 16:15:08 -0700947 if (inProgress)
948 {
949 propertiesChangedCallback(systemConfiguration, objServer);
950 return;
951 }
952 inProgress = true;
953
James Feist8f2710a2018-05-09 17:18:55 -0700954 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -0700955 auto missingConfigurations = std::make_shared<nlohmann::json>();
956 *missingConfigurations = systemConfiguration;
957
James Feist8f2710a2018-05-09 17:18:55 -0700958 std::list<nlohmann::json> configurations;
959 if (!findJsonFiles(configurations))
960 {
961 std::cerr << "cannot find json files\n";
James Feist2539ccd2020-05-01 16:15:08 -0700962 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -0700963 return;
964 }
965
966 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -0700967 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -0700968 objServer,
969 [&systemConfiguration, &objServer, count, oldConfiguration,
970 missingConfigurations]() {
James Feist899e17f2019-09-13 11:46:29 -0700971 // this is something that since ac has been applied to the bmc
972 // we saw, and we no longer see it
973 bool powerOff = !isPowerOn();
974 for (const auto& item : missingConfigurations->items())
975 {
Andrew Jeffery55192932022-03-24 12:29:27 +1030976 if (powerOff && deviceRequiresPowerOn(item.value()))
James Feist899e17f2019-09-13 11:46:29 -0700977 {
978 // power not on yet, don't know if it's there or not
979 continue;
980 }
Andrew Jeffery2f750d22022-03-24 14:32:57 +1030981 auto& ifaces = getDeviceInterfaces(item.value());
James Feist899e17f2019-09-13 11:46:29 -0700982 for (auto& iface : ifaces)
983 {
James Feist02d2b932020-02-06 16:28:48 -0800984 auto sharedPtr = iface.lock();
985 if (!sharedPtr)
986 {
987 continue; // was already deleted elsewhere
988 }
989 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -0700990 }
991 ifaces.clear();
992 systemConfiguration.erase(item.key());
993 logDeviceRemoved(item.value());
994 }
995
James Feist8f2710a2018-05-09 17:18:55 -0700996 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -0800997 for (auto it = newConfiguration.begin();
998 it != newConfiguration.end();)
999 {
1000 auto findKey = oldConfiguration.find(it.key());
1001 if (findKey != oldConfiguration.end())
1002 {
1003 it = newConfiguration.erase(it);
1004 }
1005 else
1006 {
1007 it++;
1008 }
1009 }
Andrew Jefferyace306d2022-03-25 13:18:57 +10301010 for (const auto& [_, device] : newConfiguration.items())
James Feist899e17f2019-09-13 11:46:29 -07001011 {
Andrew Jefferyace306d2022-03-25 13:18:57 +10301012 logDeviceAdded(device);
James Feist899e17f2019-09-13 11:46:29 -07001013 }
1014
James Feist2539ccd2020-05-01 16:15:08 -07001015 inProgress = false;
1016
Jonathan Doman6d649822021-05-05 16:53:04 -07001017 io.post([count, newConfiguration, &systemConfiguration,
1018 &objServer]() {
James Feist8f2710a2018-05-09 17:18:55 -07001019 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001020
Jonathan Doman6d649822021-05-05 16:53:04 -07001021 io.post([&systemConfiguration]() {
James Feistbb43d022018-06-12 15:44:33 -07001022 if (!writeJsonFiles(systemConfiguration))
1023 {
1024 std::cerr << "Error writing json files\n";
1025 }
1026 });
Jonathan Doman6d649822021-05-05 16:53:04 -07001027 io.post([count, newConfiguration, &systemConfiguration,
1028 &objServer]() {
James Feist97a63f12018-05-17 13:50:57 -07001029 postToDbus(newConfiguration, systemConfiguration,
1030 objServer);
Andrew Jefferyd235cb62022-03-24 14:22:10 +10301031 if (count == instance)
James Feist1df06a42019-04-11 14:23:04 -07001032 {
Andrew Jefferyd235cb62022-03-24 14:22:10 +10301033 startRemovedTimer(timer, systemConfiguration);
James Feist1df06a42019-04-11 14:23:04 -07001034 }
James Feist8f2710a2018-05-09 17:18:55 -07001035 });
1036 });
1037 });
1038 perfScan->run();
1039 });
James Feist75fdeeb2018-02-20 14:26:16 -08001040}
1041
James Feist98132792019-07-09 13:29:09 -07001042int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001043{
1044 // setup connection to dbus
Ed Tanous07d467b2021-02-23 14:48:37 -08001045 systemBus = std::make_shared<sdbusplus::asio::connection>(io);
1046 systemBus->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001047
Ed Tanous07d467b2021-02-23 14:48:37 -08001048 sdbusplus::asio::object_server objServer(systemBus);
James Feistfd1264a2018-05-03 12:10:00 -07001049
James Feist8f2710a2018-05-09 17:18:55 -07001050 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1051 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1052 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001053
James Feist4131aea2018-03-09 09:47:30 -08001054 // to keep reference to the match / filter objects so they don't get
1055 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001056
1057 nlohmann::json systemConfiguration = nlohmann::json::object();
1058
Brad Bishopc76af0f2020-12-04 13:50:23 -05001059 // We need a poke from DBus for static providers that create all their
1060 // objects prior to claiming a well-known name, and thus don't emit any
1061 // org.freedesktop.DBus.Properties signals. Similarly if a process exits
1062 // for any reason, expected or otherwise, we'll need a poke to remove
1063 // entities from DBus.
1064 sdbusplus::bus::match::match nameOwnerChangedMatch(
Ed Tanous07d467b2021-02-23 14:48:37 -08001065 static_cast<sdbusplus::bus::bus&>(*systemBus),
Brad Bishopc76af0f2020-12-04 13:50:23 -05001066 sdbusplus::bus::match::rules::nameOwnerChanged(),
1067 [&](sdbusplus::message::message&) {
1068 propertiesChangedCallback(systemConfiguration, objServer);
1069 });
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001070 // We also need a poke from DBus when new interfaces are created or
1071 // destroyed.
1072 sdbusplus::bus::match::match interfacesAddedMatch(
Ed Tanous07d467b2021-02-23 14:48:37 -08001073 static_cast<sdbusplus::bus::bus&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001074 sdbusplus::bus::match::rules::interfacesAdded(),
1075 [&](sdbusplus::message::message&) {
1076 propertiesChangedCallback(systemConfiguration, objServer);
1077 });
1078 sdbusplus::bus::match::match interfacesRemovedMatch(
Ed Tanous07d467b2021-02-23 14:48:37 -08001079 static_cast<sdbusplus::bus::bus&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001080 sdbusplus::bus::match::rules::interfacesRemoved(),
1081 [&](sdbusplus::message::message&) {
1082 propertiesChangedCallback(systemConfiguration, objServer);
1083 });
Brad Bishopc76af0f2020-12-04 13:50:23 -05001084
James Feist4dc617b2020-05-01 09:54:47 -07001085 io.post(
1086 [&]() { propertiesChangedCallback(systemConfiguration, objServer); });
James Feist4131aea2018-03-09 09:47:30 -08001087
James Feistfd1264a2018-05-03 12:10:00 -07001088 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001089 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001090 });
James Feist8f2710a2018-05-09 17:18:55 -07001091 entityIface->initialize();
1092
James Feist1df06a42019-04-11 14:23:04 -07001093 if (fwVersionIsSame())
1094 {
1095 if (std::filesystem::is_regular_file(currentConfiguration))
1096 {
1097 // this file could just be deleted, but it's nice for debug
1098 std::filesystem::create_directory(tempConfigDir);
1099 std::filesystem::remove(lastConfiguration);
1100 std::filesystem::copy(currentConfiguration, lastConfiguration);
1101 std::filesystem::remove(currentConfiguration);
1102
1103 std::ifstream jsonStream(lastConfiguration);
1104 if (jsonStream.good())
1105 {
1106 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1107 if (data.is_discarded())
1108 {
1109 std::cerr << "syntax error in " << lastConfiguration
1110 << "\n";
1111 }
1112 else
1113 {
1114 lastJson = std::move(data);
1115 }
1116 }
1117 else
1118 {
1119 std::cerr << "unable to open " << lastConfiguration << "\n";
1120 }
1121 }
1122 }
1123 else
1124 {
1125 // not an error, just logging at this level to make it in the journal
1126 std::cerr << "Clearing previous configuration\n";
1127 std::filesystem::remove(currentConfiguration);
1128 }
1129
1130 // some boards only show up after power is on, we want to not say they are
1131 // removed until the same state happens
Ed Tanous07d467b2021-02-23 14:48:37 -08001132 setupPowerMatch(systemBus);
James Feist1df06a42019-04-11 14:23:04 -07001133
James Feist1b2e2242018-01-30 13:45:19 -08001134 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001135
1136 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001137}