blob: d69af0bf3adace0466edae442af0cc872f64ed93 [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>
Andrew Jefferye35d0ac2022-03-24 15:53:13 +103041#include <functional>
James Feista465ccc2019-02-08 12:51:01 -080042#include <iostream>
Andrew Jeffery666583b2021-12-01 15:50:12 +103043#include <map>
James Feista465ccc2019-02-08 12:51:01 -080044#include <regex>
James Feista465ccc2019-02-08 12:51:01 -080045#include <variant>
Andrew Jefferya9c58922021-06-01 09:28:59 +093046constexpr const char* hostConfigurationDirectory = SYSCONF_DIR "configurations";
James Feista465ccc2019-02-08 12:51:01 -080047constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
48constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070049constexpr const char* tempConfigDir = "/tmp/configuration/";
50constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
51constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080052constexpr const char* globalSchema = "global.json";
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 Feistd58879a82019-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 Feistd58879a82019-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 Feistd58879a82019-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 Feistd58879a82019-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 Feistd58879a82019-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{
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030284 for (auto& [key, value] : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800285 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030286 auto type = value.type();
James Feist8f2710a2018-05-09 17:18:55 -0700287 bool array = false;
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030288 if (value.type() == nlohmann::json::value_t::array)
James Feist8f2710a2018-05-09 17:18:55 -0700289 {
290 array = true;
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030291 if (!value.size())
James Feist8f2710a2018-05-09 17:18:55 -0700292 {
293 continue;
294 }
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030295 type = value[0].type();
James Feist8f2710a2018-05-09 17:18:55 -0700296 bool isLegal = true;
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030297 for (const auto& arrayItem : 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 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030307 std::cerr << "dbus format error" << value << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700308 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 }
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030315
316 std::string path = jsonPointerPath;
317 path.append("/").append(key);
James Feistbb43d022018-06-12 15:44:33 -0700318 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
319 {
320 // all setable numbers are doubles as it is difficult to always
321 // create a configuration file with all whole numbers as decimals
322 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700323 if (array)
324 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030325 if (value[0].is_number())
James Feistebcc26b2019-03-22 12:30:43 -0700326 {
327 type = nlohmann::json::value_t::number_float;
328 }
329 }
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030330 else if (value.is_number())
James Feistbb43d022018-06-12 15:44:33 -0700331 {
332 type = nlohmann::json::value_t::number_float;
333 }
334 }
335
James Feist8f2710a2018-05-09 17:18:55 -0700336 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800337 {
James Feist9eb0b582018-04-27 12:15:46 -0700338 case (nlohmann::json::value_t::boolean):
339 {
James Feist8f2710a2018-05-09 17:18:55 -0700340 if (array)
341 {
342 // todo: array of bool isn't detected correctly by
343 // sdbusplus, change it to numbers
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030344 addArrayToDbus<uint64_t>(key, value, iface.get(),
345 permission, systemConfiguration,
346 path);
James Feist8f2710a2018-05-09 17:18:55 -0700347 }
James Feistbb43d022018-06-12 15:44:33 -0700348
James Feist97a63f12018-05-17 13:50:57 -0700349 else
350 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030351 addProperty(key, value.get<bool>(), iface.get(),
352 systemConfiguration, path, permission);
James Feist97a63f12018-05-17 13:50:57 -0700353 }
James Feist9eb0b582018-04-27 12:15:46 -0700354 break;
355 }
356 case (nlohmann::json::value_t::number_integer):
357 {
James Feist8f2710a2018-05-09 17:18:55 -0700358 if (array)
359 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030360 addArrayToDbus<int64_t>(key, value, iface.get(), permission,
Andrew Jeffery029ee282022-03-25 13:11:36 +1030361 systemConfiguration, path);
James Feist97a63f12018-05-17 13:50:57 -0700362 }
363 else
364 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030365 addProperty(key, value.get<int64_t>(), iface.get(),
366 systemConfiguration, path,
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 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030375 addArrayToDbus<uint64_t>(key, value, iface.get(),
376 permission, systemConfiguration,
377 path);
James Feist97a63f12018-05-17 13:50:57 -0700378 }
379 else
380 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030381 addProperty(key, value.get<uint64_t>(), iface.get(),
Andrew Jeffery029ee282022-03-25 13:11:36 +1030382 systemConfiguration, path,
James Feistbb43d022018-06-12 15:44:33 -0700383 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700384 }
James Feist9eb0b582018-04-27 12:15:46 -0700385 break;
386 }
387 case (nlohmann::json::value_t::number_float):
388 {
James Feist8f2710a2018-05-09 17:18:55 -0700389 if (array)
390 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030391 addArrayToDbus<double>(key, value, iface.get(), permission,
Andrew Jeffery029ee282022-03-25 13:11:36 +1030392 systemConfiguration, path);
James Feist8f2710a2018-05-09 17:18:55 -0700393 }
James Feistbb43d022018-06-12 15:44:33 -0700394
James Feist97a63f12018-05-17 13:50:57 -0700395 else
396 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030397 addProperty(key, value.get<double>(), iface.get(),
398 systemConfiguration, path, permission);
James Feist97a63f12018-05-17 13:50:57 -0700399 }
James Feist9eb0b582018-04-27 12:15:46 -0700400 break;
401 }
402 case (nlohmann::json::value_t::string):
403 {
James Feist8f2710a2018-05-09 17:18:55 -0700404 if (array)
405 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030406 addArrayToDbus<std::string>(key, value, iface.get(),
407 permission, systemConfiguration,
408 path);
James Feist97a63f12018-05-17 13:50:57 -0700409 }
410 else
411 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030412 addProperty(key, value.get<std::string>(), iface.get(),
413 systemConfiguration, path, permission);
James Feist97a63f12018-05-17 13:50:57 -0700414 }
James Feist9eb0b582018-04-27 12:15:46 -0700415 break;
416 }
James Feist0eb40352019-04-09 14:44:04 -0700417 default:
418 {
James Feista218ddb2019-04-11 14:01:31 -0700419 std::cerr << "Unexpected json type in system configuration "
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030420 << key << ": " << value.type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700421 break;
422 }
James Feist1b2e2242018-01-30 13:45:19 -0800423 }
424 }
James Feistc6248a52018-08-14 10:09:45 -0700425 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
426 {
427 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
428 systemConfiguration);
429 }
James Feist8f2710a2018-05-09 17:18:55 -0700430 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800431}
432
James Feista465ccc2019-02-08 12:51:01 -0800433sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700434{
435 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
436 interface) != settableInterfaces.end()
437 ? sdbusplus::asio::PropertyPermission::readWrite
438 : sdbusplus::asio::PropertyPermission::readOnly;
439}
440
James Feista465ccc2019-02-08 12:51:01 -0800441void createAddObjectMethod(const std::string& jsonPointerPath,
442 const std::string& path,
443 nlohmann::json& systemConfiguration,
James Feistd58879a82019-09-11 11:26:07 -0700444 sdbusplus::asio::object_server& objServer,
445 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700446{
James Feistd58879a82019-09-11 11:26:07 -0700447 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
448 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700449
450 iface->register_method(
451 "AddObject",
452 [&systemConfiguration, &objServer,
James Feistd58879a82019-09-11 11:26:07 -0700453 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
454 board](const boost::container::flat_map<std::string, JsonVariantType>&
455 data) {
James Feist68500ff2018-08-08 15:40:42 -0700456 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800457 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700458 auto findExposes = base.find("Exposes");
459
460 if (findExposes == base.end())
461 {
462 throw std::invalid_argument("Entity must have children.");
463 }
464
465 // this will throw invalid-argument to sdbusplus if invalid json
466 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800467 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700468 {
James Feista465ccc2019-02-08 12:51:01 -0800469 nlohmann::json& newJson = newData[item.first];
470 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
471 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700472 }
473
474 auto findName = newData.find("Name");
475 auto findType = newData.find("Type");
476 if (findName == newData.end() || findType == newData.end())
477 {
478 throw std::invalid_argument("AddObject missing Name or Type");
479 }
James Feista465ccc2019-02-08 12:51:01 -0800480 const std::string* type = findType->get_ptr<const std::string*>();
481 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700482 if (type == nullptr || name == nullptr)
483 {
484 throw std::invalid_argument("Type and Name must be a string.");
485 }
486
James Feist02d2b932020-02-06 16:28:48 -0800487 bool foundNull = false;
James Feist68500ff2018-08-08 15:40:42 -0700488 size_t lastIndex = 0;
489 // we add in the "exposes"
James Feist02d2b932020-02-06 16:28:48 -0800490 for (const auto& expose : *findExposes)
James Feist68500ff2018-08-08 15:40:42 -0700491 {
James Feist02d2b932020-02-06 16:28:48 -0800492 if (expose.is_null())
493 {
494 foundNull = true;
495 continue;
496 }
497
498 if (expose["Name"] == *name && expose["Type"] == *type)
James Feist68500ff2018-08-08 15:40:42 -0700499 {
500 throw std::invalid_argument(
501 "Field already in JSON, not adding");
502 }
James Feist02d2b932020-02-06 16:28:48 -0800503
504 if (foundNull)
505 {
506 continue;
507 }
508
James Feist68500ff2018-08-08 15:40:42 -0700509 lastIndex++;
510 }
511
512 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
513 *type + ".json");
514 // todo(james) we might want to also make a list of 'can add'
515 // interfaces but for now I think the assumption if there is a
516 // schema avaliable that it is allowed to update is fine
517 if (!schemaFile.good())
518 {
519 throw std::invalid_argument(
520 "No schema avaliable, cannot validate.");
521 }
522 nlohmann::json schema =
523 nlohmann::json::parse(schemaFile, nullptr, false);
524 if (schema.is_discarded())
525 {
526 std::cerr << "Schema not legal" << *type << ".json\n";
527 throw DBusInternalError();
528 }
529 if (!validateJson(schema, newData))
530 {
531 throw std::invalid_argument("Data does not match schema");
532 }
James Feist02d2b932020-02-06 16:28:48 -0800533 if (foundNull)
534 {
535 findExposes->at(lastIndex) = newData;
536 }
537 else
538 {
539 findExposes->push_back(newData);
540 }
James Feist68500ff2018-08-08 15:40:42 -0700541 if (!writeJsonFiles(systemConfiguration))
542 {
543 std::cerr << "Error writing json files\n";
544 throw DBusInternalError();
545 }
546 std::string dbusName = *name;
547
548 std::regex_replace(dbusName.begin(), dbusName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800549 dbusName.end(), illegalDbusMemberRegex, "_");
James Feistd58879a82019-09-11 11:26:07 -0700550
551 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
552 createInterface(objServer, path + "/" + dbusName,
553 "xyz.openbmc_project.Configuration." + *type,
James Feist02d2b932020-02-06 16:28:48 -0800554 board, true);
James Feist68500ff2018-08-08 15:40:42 -0700555 // permission is read-write, as since we just created it, must be
556 // runtime modifiable
557 populateInterfaceFromJson(
558 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700559 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700560 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700561 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700562 });
563 iface->initialize();
564}
565
James Feista465ccc2019-02-08 12:51:01 -0800566void postToDbus(const nlohmann::json& newConfiguration,
567 nlohmann::json& systemConfiguration,
568 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800569
James Feist1b2e2242018-01-30 13:45:19 -0800570{
James Feist97a63f12018-05-17 13:50:57 -0700571 // iterate through boards
Andrew Jeffery13132df2022-03-25 13:29:41 +1030572 for (auto& [boardId, boardConfig] : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800573 {
Andrew Jeffery13132df2022-03-25 13:29:41 +1030574 std::string boardKey = boardConfig["Name"];
575 std::string boardKeyOrig = boardConfig["Name"];
576 std::string jsonPointerPath = "/" + boardId;
James Feist97a63f12018-05-17 13:50:57 -0700577 // loop through newConfiguration, but use values from system
578 // configuration to be able to modify via dbus later
Andrew Jeffery13132df2022-03-25 13:29:41 +1030579 auto boardValues = systemConfiguration[boardId];
James Feistd63d18a2018-07-19 15:23:45 -0700580 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800581 std::string boardType;
582 if (findBoardType != boardValues.end() &&
583 findBoardType->type() == nlohmann::json::value_t::string)
584 {
585 boardType = findBoardType->get<std::string>();
586 std::regex_replace(boardType.begin(), boardType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800587 boardType.end(), illegalDbusMemberRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800588 }
589 else
590 {
591 std::cerr << "Unable to find type for " << boardKey
592 << " reverting to Chassis.\n";
593 boardType = "Chassis";
594 }
James Feist11be6672018-04-06 14:05:32 -0700595 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800596
597 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800598 illegalDbusMemberRegex, "_");
599 std::string boardName = "/xyz/openbmc_project/inventory/system/";
600 boardName += boardtypeLower;
601 boardName += "/";
602 boardName += boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800603
James Feistd58879a82019-09-11 11:26:07 -0700604 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
605 createInterface(objServer, boardName,
606 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700607
James Feistd58879a82019-09-11 11:26:07 -0700608 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
609 createInterface(objServer, boardName,
610 "xyz.openbmc_project.Inventory.Item." + boardType,
611 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -0700612
James Feist68500ff2018-08-08 15:40:42 -0700613 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a82019-09-11 11:26:07 -0700614 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -0700615
James Feist97a63f12018-05-17 13:50:57 -0700616 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700617 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700618 jsonPointerPath += "/";
619 // iterate through board properties
Andrew Jefferya96950d2022-03-25 13:32:46 +1030620 for (auto& [propName, propValue] : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700621 {
Andrew Jefferya96950d2022-03-25 13:32:46 +1030622 if (propValue.type() == nlohmann::json::value_t::object)
James Feist11be6672018-04-06 14:05:32 -0700623 {
James Feistd58879a82019-09-11 11:26:07 -0700624 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
Andrew Jefferya96950d2022-03-25 13:32:46 +1030625 createInterface(objServer, boardName, propName,
James Feistd58879a82019-09-11 11:26:07 -0700626 boardKeyOrig);
627
James Feistc6248a52018-08-14 10:09:45 -0700628 populateInterfaceFromJson(systemConfiguration,
Andrew Jefferya96950d2022-03-25 13:32:46 +1030629 jsonPointerPath + propName, iface,
630 propValue, objServer);
James Feist11be6672018-04-06 14:05:32 -0700631 }
632 }
James Feist97a63f12018-05-17 13:50:57 -0700633
James Feist1e3e6982018-08-03 16:09:28 -0700634 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800635 if (exposes == boardValues.end())
636 {
637 continue;
638 }
James Feist97a63f12018-05-17 13:50:57 -0700639 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700640 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700641
642 // store the board level pointer so we can modify it on the way down
643 std::string jsonPointerPathBoard = jsonPointerPath;
644 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -0800645 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -0800646 {
James Feist97a63f12018-05-17 13:50:57 -0700647 exposesIndex++;
648 jsonPointerPath = jsonPointerPathBoard;
649 jsonPointerPath += std::to_string(exposesIndex);
650
James Feistd63d18a2018-07-19 15:23:45 -0700651 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800652 if (findName == item.end())
653 {
654 std::cerr << "cannot find name in field " << item << "\n";
655 continue;
656 }
James Feist1e3e6982018-08-03 16:09:28 -0700657 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800658 // if status is not found it is assumed to be status = 'okay'
659 if (findStatus != item.end())
660 {
661 if (*findStatus == "disabled")
662 {
663 continue;
664 }
665 }
James Feistd63d18a2018-07-19 15:23:45 -0700666 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800667 std::string itemType;
668 if (findType != item.end())
669 {
670 itemType = findType->get<std::string>();
671 std::regex_replace(itemType.begin(), itemType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800672 itemType.end(), illegalDbusPathRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800673 }
674 else
675 {
676 itemType = "unknown";
677 }
678 std::string itemName = findName->get<std::string>();
679 std::regex_replace(itemName.begin(), itemName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800680 itemName.end(), illegalDbusMemberRegex, "_");
681 std::string ifacePath = boardName;
682 ifacePath += "/";
683 ifacePath += itemName;
James Feistc6248a52018-08-14 10:09:45 -0700684
James Feistd58879a82019-09-11 11:26:07 -0700685 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
Ed Tanous07d467b2021-02-23 14:48:37 -0800686 createInterface(objServer, ifacePath,
James Feistd58879a82019-09-11 11:26:07 -0700687 "xyz.openbmc_project.Configuration." + itemType,
688 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -0800689
James Feist97a63f12018-05-17 13:50:57 -0700690 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700691 itemIface, item, objServer,
692 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -0800693
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030694 for (auto& [name, config] : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800695 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030696 jsonPointerPath = jsonPointerPathBoard;
697 jsonPointerPath.append(std::to_string(exposesIndex))
698 .append("/")
699 .append(name);
700 if (config.type() == nlohmann::json::value_t::object)
James Feist1b2e2242018-01-30 13:45:19 -0800701 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030702 std::string ifaceName =
703 "xyz.openbmc_project.Configuration.";
704 ifaceName.append(itemType).append(".").append(name);
James Feist97a63f12018-05-17 13:50:57 -0700705
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030706 std::shared_ptr<sdbusplus::asio::dbus_interface>
707 objectIface = createInterface(objServer, ifacePath,
708 ifaceName, boardKeyOrig);
709
710 populateInterfaceFromJson(
711 systemConfiguration, jsonPointerPath, objectIface,
712 config, objServer, getPermission(name));
James Feist1b2e2242018-01-30 13:45:19 -0800713 }
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030714 else if (config.type() == nlohmann::json::value_t::array)
James Feist1b2e2242018-01-30 13:45:19 -0800715 {
716 size_t index = 0;
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030717 if (!config.size())
James Feist1b2e2242018-01-30 13:45:19 -0800718 {
James Feist8f2710a2018-05-09 17:18:55 -0700719 continue;
720 }
721 bool isLegal = true;
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030722 auto type = config[0].type();
James Feist8f2710a2018-05-09 17:18:55 -0700723 if (type != nlohmann::json::value_t::object)
724 {
725 continue;
726 }
727
728 // verify legal json
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030729 for (const auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700730 {
731 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800732 {
James Feist8f2710a2018-05-09 17:18:55 -0700733 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800734 break;
735 }
James Feist8f2710a2018-05-09 17:18:55 -0700736 }
737 if (!isLegal)
738 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030739 std::cerr << "dbus format error" << config << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700740 break;
741 }
742
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030743 for (auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700744 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030745 std::string ifaceName =
746 "xyz.openbmc_project.Configuration.";
747 ifaceName.append(itemType).append(".").append(name);
748 ifaceName.append(std::to_string(index));
James Feist97a63f12018-05-17 13:50:57 -0700749
James Feistd58879a82019-09-11 11:26:07 -0700750 std::shared_ptr<sdbusplus::asio::dbus_interface>
751 objectIface = createInterface(
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030752 objServer, ifacePath, ifaceName, boardKeyOrig);
James Feistd58879a82019-09-11 11:26:07 -0700753
James Feistc6248a52018-08-14 10:09:45 -0700754 populateInterfaceFromJson(
755 systemConfiguration,
756 jsonPointerPath + "/" + std::to_string(index),
757 objectIface, arrayItem, objServer,
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030758 getPermission(name));
James Feistbb43d022018-06-12 15:44:33 -0700759 index++;
James Feist1b2e2242018-01-30 13:45:19 -0800760 }
761 }
762 }
763 }
764 }
765}
766
James Feist8f2710a2018-05-09 17:18:55 -0700767// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -0800768bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -0800769{
770 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -0800771 std::vector<std::filesystem::path> jsonPaths;
Andrew Jefferya9c58922021-06-01 09:28:59 +0930772 if (!findFiles(
773 std::vector<std::filesystem::path>{configurationDirectory,
774 hostConfigurationDirectory},
775 R"(.*\.json)", jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -0800776 {
777 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -0700778 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -0800779 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800780 }
James Feistb4383f42018-08-06 16:54:10 -0700781
782 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
783 globalSchema);
784 if (!schemaStream.good())
785 {
786 std::cerr
787 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
788 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -0800789 return false;
James Feistb4383f42018-08-06 16:54:10 -0700790 }
791 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
792 if (schema.is_discarded())
793 {
794 std::cerr
795 << "Illegal schema file detected, cannot validate JSON, exiting\n";
796 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -0800797 return false;
James Feistb4383f42018-08-06 16:54:10 -0700798 }
799
James Feista465ccc2019-02-08 12:51:01 -0800800 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -0800801 {
802 std::ifstream jsonStream(jsonPath.c_str());
803 if (!jsonStream.good())
804 {
805 std::cerr << "unable to open " << jsonPath.string() << "\n";
806 continue;
807 }
808 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
809 if (data.is_discarded())
810 {
811 std::cerr << "syntax error in " << jsonPath.string() << "\n";
812 continue;
813 }
James Feist8da99192019-01-24 08:20:16 -0800814 /*
815 * todo(james): reenable this once less things are in flight
816 *
James Feistb4383f42018-08-06 16:54:10 -0700817 if (!validateJson(schema, data))
818 {
819 std::cerr << "Error validating " << jsonPath.string() << "\n";
820 continue;
821 }
James Feist8da99192019-01-24 08:20:16 -0800822 */
James Feistb4383f42018-08-06 16:54:10 -0700823
James Feist3cb5fec2018-01-23 14:41:51 -0800824 if (data.type() == nlohmann::json::value_t::array)
825 {
James Feista465ccc2019-02-08 12:51:01 -0800826 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -0800827 {
828 configurations.emplace_back(d);
829 }
830 }
831 else
832 {
833 configurations.emplace_back(data);
834 }
835 }
Ed Tanous072e25d2018-12-16 21:45:20 -0800836 return true;
James Feist75fdeeb2018-02-20 14:26:16 -0800837}
James Feist3cb5fec2018-01-23 14:41:51 -0800838
Andrew Jeffery55192932022-03-24 12:29:27 +1030839static bool deviceRequiresPowerOn(const nlohmann::json& entity)
840{
841 auto powerState = entity.find("PowerState");
Andrew Jefferyb6209442022-03-24 12:36:20 +1030842 if (powerState == entity.end())
Andrew Jeffery55192932022-03-24 12:29:27 +1030843 {
Andrew Jefferyb6209442022-03-24 12:36:20 +1030844 return false;
Andrew Jeffery55192932022-03-24 12:29:27 +1030845 }
846
Andrew Jefferyb6209442022-03-24 12:36:20 +1030847 auto ptr = powerState->get_ptr<const std::string*>();
848 if (!ptr)
849 {
850 return false;
851 }
852
853 return *ptr == "On" || *ptr == "BiosPost";
Andrew Jeffery55192932022-03-24 12:29:27 +1030854}
855
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030856static void pruneDevice(const nlohmann::json& systemConfiguration,
857 const bool powerOff, const bool scannedPowerOff,
858 const std::string& name, const nlohmann::json& device)
859{
860 if (systemConfiguration.contains(name))
861 {
862 return;
863 }
864
Andrew Jeffery4db38bc2022-03-24 13:42:41 +1030865 if (deviceRequiresPowerOn(device) && (powerOff || scannedPowerOff))
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030866 {
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030867 return;
868 }
869
870 logDeviceRemoved(device);
871}
872
James Feistb1728ca2020-04-30 15:40:55 -0700873void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -0700874 nlohmann::json& systemConfiguration)
875{
876 static bool scannedPowerOff = false;
877 static bool scannedPowerOn = false;
878
James Feistfb00f392019-06-25 14:16:48 -0700879 if (systemConfiguration.empty() || lastJson.empty())
880 {
881 return; // not ready yet
882 }
James Feist1df06a42019-04-11 14:23:04 -0700883 if (scannedPowerOn)
884 {
885 return;
886 }
887
888 if (!isPowerOn() && scannedPowerOff)
889 {
890 return;
891 }
892
James Feistb1728ca2020-04-30 15:40:55 -0700893 timer.expires_after(std::chrono::seconds(10));
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030894 timer.async_wait(
895 [&systemConfiguration](const boost::system::error_code& ec) {
896 if (ec == boost::asio::error::operation_aborted)
James Feist1a996582019-05-14 15:10:06 -0700897 {
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030898 return;
James Feist1df06a42019-04-11 14:23:04 -0700899 }
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030900
901 bool powerOff = !isPowerOn();
Andrew Jefferya82c55d2022-03-24 14:17:28 +1030902 for (const auto& [name, device] : lastJson.items())
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030903 {
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030904 pruneDevice(systemConfiguration, powerOff, scannedPowerOff,
905 name, device);
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030906 }
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030907
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030908 scannedPowerOff = true;
909 if (!powerOff)
910 {
911 scannedPowerOn = true;
912 }
913 });
James Feist1df06a42019-04-11 14:23:04 -0700914}
915
Andrew Jeffery2f750d22022-03-24 14:32:57 +1030916static std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
917 getDeviceInterfaces(const nlohmann::json& device)
918{
919 return inventory[device["Name"].get<std::string>()];
920}
921
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030922static void pruneConfiguration(nlohmann::json& systemConfiguration,
923 sdbusplus::asio::object_server& objServer,
924 bool powerOff, const std::string& name,
925 const nlohmann::json& device)
926{
927 if (powerOff && deviceRequiresPowerOn(device))
928 {
929 // power not on yet, don't know if it's there or not
930 return;
931 }
932
933 auto& ifaces = getDeviceInterfaces(device);
934 for (auto& iface : ifaces)
935 {
936 auto sharedPtr = iface.lock();
937 if (!!sharedPtr)
938 {
939 objServer.remove_interface(sharedPtr);
940 }
941 }
942
943 ifaces.clear();
944 systemConfiguration.erase(name);
945 logDeviceRemoved(device);
946}
947
Andrew Jeffery7c4cbbe2022-03-24 15:27:10 +1030948static void deriveNewConfiguration(const nlohmann::json& oldConfiguration,
949 nlohmann::json& newConfiguration)
950{
951 for (auto it = newConfiguration.begin(); it != newConfiguration.end();)
952 {
953 auto findKey = oldConfiguration.find(it.key());
954 if (findKey != oldConfiguration.end())
955 {
956 it = newConfiguration.erase(it);
957 }
958 else
959 {
960 it++;
961 }
962 }
963}
964
Andrew Jefferye35d0ac2022-03-24 15:53:13 +1030965static void publishNewConfiguration(
966 const size_t& instance, const size_t count,
967 boost::asio::steady_timer& timer, nlohmann::json& systemConfiguration,
968 // Gerrit discussion:
969 // https://gerrit.openbmc-project.xyz/c/openbmc/entity-manager/+/52316/6
970 //
971 // Discord discussion:
972 // https://discord.com/channels/775381525260664832/867820390406422538/958048437729910854
973 //
974 // NOLINTNEXTLINE(performance-unnecessary-value-param)
975 const nlohmann::json newConfiguration,
976 sdbusplus::asio::object_server& objServer)
977{
978 loadOverlays(newConfiguration);
979
980 io.post([systemConfiguration]() {
981 if (!writeJsonFiles(systemConfiguration))
982 {
983 std::cerr << "Error writing json files\n";
984 }
985 });
986
987 io.post([&instance, count, &timer, newConfiguration, &systemConfiguration,
988 &objServer]() {
989 postToDbus(newConfiguration, systemConfiguration, objServer);
990 if (count == instance)
991 {
992 startRemovedTimer(timer, systemConfiguration);
993 }
994 });
995}
996
James Feist8f2710a2018-05-09 17:18:55 -0700997// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -0700998void propertiesChangedCallback(nlohmann::json& systemConfiguration,
999 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001000{
James Feist2539ccd2020-05-01 16:15:08 -07001001 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -07001002 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001003 static size_t instance = 0;
1004 instance++;
1005 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001006
James Feistb1728ca2020-04-30 15:40:55 -07001007 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001008
1009 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -07001010 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -07001011 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001012 if (ec == boost::asio::error::operation_aborted)
1013 {
1014 // we were cancelled
1015 return;
1016 }
Ed Tanous07d467b2021-02-23 14:48:37 -08001017 if (ec)
James Feist8f2710a2018-05-09 17:18:55 -07001018 {
1019 std::cerr << "async wait error " << ec << "\n";
1020 return;
1021 }
1022
James Feist2539ccd2020-05-01 16:15:08 -07001023 if (inProgress)
1024 {
1025 propertiesChangedCallback(systemConfiguration, objServer);
1026 return;
1027 }
1028 inProgress = true;
1029
James Feist8f2710a2018-05-09 17:18:55 -07001030 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001031 auto missingConfigurations = std::make_shared<nlohmann::json>();
1032 *missingConfigurations = systemConfiguration;
1033
James Feist8f2710a2018-05-09 17:18:55 -07001034 std::list<nlohmann::json> configurations;
1035 if (!findJsonFiles(configurations))
1036 {
1037 std::cerr << "cannot find json files\n";
James Feist2539ccd2020-05-01 16:15:08 -07001038 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -07001039 return;
1040 }
1041
1042 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001043 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001044 objServer,
1045 [&systemConfiguration, &objServer, count, oldConfiguration,
1046 missingConfigurations]() {
James Feist899e17f2019-09-13 11:46:29 -07001047 // this is something that since ac has been applied to the bmc
1048 // we saw, and we no longer see it
1049 bool powerOff = !isPowerOn();
Andrew Jeffery6cbbc092022-03-24 14:35:14 +10301050 for (const auto& [name, device] :
1051 missingConfigurations->items())
James Feist899e17f2019-09-13 11:46:29 -07001052 {
Andrew Jeffery0d0c1462022-03-24 15:26:39 +10301053 pruneConfiguration(systemConfiguration, objServer, powerOff,
1054 name, device);
James Feist899e17f2019-09-13 11:46:29 -07001055 }
1056
James Feist8f2710a2018-05-09 17:18:55 -07001057 nlohmann::json newConfiguration = systemConfiguration;
Andrew Jeffery7c4cbbe2022-03-24 15:27:10 +10301058
1059 deriveNewConfiguration(oldConfiguration, newConfiguration);
1060
Andrew Jefferyace306d2022-03-25 13:18:57 +10301061 for (const auto& [_, device] : newConfiguration.items())
James Feist899e17f2019-09-13 11:46:29 -07001062 {
Andrew Jefferyace306d2022-03-25 13:18:57 +10301063 logDeviceAdded(device);
James Feist899e17f2019-09-13 11:46:29 -07001064 }
1065
James Feist2539ccd2020-05-01 16:15:08 -07001066 inProgress = false;
1067
Andrew Jefferye35d0ac2022-03-24 15:53:13 +10301068 io.post(std::bind_front(
1069 publishNewConfiguration, std::ref(instance), count,
1070 std::ref(timer), std::ref(systemConfiguration),
1071 newConfiguration, std::ref(objServer)));
James Feist8f2710a2018-05-09 17:18:55 -07001072 });
1073 perfScan->run();
1074 });
James Feist75fdeeb2018-02-20 14:26:16 -08001075}
1076
James Feist98132792019-07-09 13:29:09 -07001077int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001078{
1079 // setup connection to dbus
Ed Tanous07d467b2021-02-23 14:48:37 -08001080 systemBus = std::make_shared<sdbusplus::asio::connection>(io);
1081 systemBus->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001082
Ed Tanous07d467b2021-02-23 14:48:37 -08001083 sdbusplus::asio::object_server objServer(systemBus);
James Feistfd1264a2018-05-03 12:10:00 -07001084
James Feist8f2710a2018-05-09 17:18:55 -07001085 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1086 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1087 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001088
James Feist4131aea2018-03-09 09:47:30 -08001089 // to keep reference to the match / filter objects so they don't get
1090 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001091
1092 nlohmann::json systemConfiguration = nlohmann::json::object();
1093
Brad Bishopc76af0f2020-12-04 13:50:23 -05001094 // We need a poke from DBus for static providers that create all their
1095 // objects prior to claiming a well-known name, and thus don't emit any
1096 // org.freedesktop.DBus.Properties signals. Similarly if a process exits
1097 // for any reason, expected or otherwise, we'll need a poke to remove
1098 // entities from DBus.
1099 sdbusplus::bus::match::match nameOwnerChangedMatch(
Ed Tanous07d467b2021-02-23 14:48:37 -08001100 static_cast<sdbusplus::bus::bus&>(*systemBus),
Brad Bishopc76af0f2020-12-04 13:50:23 -05001101 sdbusplus::bus::match::rules::nameOwnerChanged(),
1102 [&](sdbusplus::message::message&) {
1103 propertiesChangedCallback(systemConfiguration, objServer);
1104 });
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001105 // We also need a poke from DBus when new interfaces are created or
1106 // destroyed.
1107 sdbusplus::bus::match::match interfacesAddedMatch(
Ed Tanous07d467b2021-02-23 14:48:37 -08001108 static_cast<sdbusplus::bus::bus&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001109 sdbusplus::bus::match::rules::interfacesAdded(),
1110 [&](sdbusplus::message::message&) {
1111 propertiesChangedCallback(systemConfiguration, objServer);
1112 });
1113 sdbusplus::bus::match::match interfacesRemovedMatch(
Ed Tanous07d467b2021-02-23 14:48:37 -08001114 static_cast<sdbusplus::bus::bus&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001115 sdbusplus::bus::match::rules::interfacesRemoved(),
1116 [&](sdbusplus::message::message&) {
1117 propertiesChangedCallback(systemConfiguration, objServer);
1118 });
Brad Bishopc76af0f2020-12-04 13:50:23 -05001119
James Feist4dc617b2020-05-01 09:54:47 -07001120 io.post(
1121 [&]() { propertiesChangedCallback(systemConfiguration, objServer); });
James Feist4131aea2018-03-09 09:47:30 -08001122
James Feistfd1264a2018-05-03 12:10:00 -07001123 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001124 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001125 });
James Feist8f2710a2018-05-09 17:18:55 -07001126 entityIface->initialize();
1127
James Feist1df06a42019-04-11 14:23:04 -07001128 if (fwVersionIsSame())
1129 {
1130 if (std::filesystem::is_regular_file(currentConfiguration))
1131 {
1132 // this file could just be deleted, but it's nice for debug
1133 std::filesystem::create_directory(tempConfigDir);
1134 std::filesystem::remove(lastConfiguration);
1135 std::filesystem::copy(currentConfiguration, lastConfiguration);
1136 std::filesystem::remove(currentConfiguration);
1137
1138 std::ifstream jsonStream(lastConfiguration);
1139 if (jsonStream.good())
1140 {
1141 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1142 if (data.is_discarded())
1143 {
1144 std::cerr << "syntax error in " << lastConfiguration
1145 << "\n";
1146 }
1147 else
1148 {
1149 lastJson = std::move(data);
1150 }
1151 }
1152 else
1153 {
1154 std::cerr << "unable to open " << lastConfiguration << "\n";
1155 }
1156 }
1157 }
1158 else
1159 {
1160 // not an error, just logging at this level to make it in the journal
1161 std::cerr << "Clearing previous configuration\n";
1162 std::filesystem::remove(currentConfiguration);
1163 }
1164
1165 // some boards only show up after power is on, we want to not say they are
1166 // removed until the same state happens
Ed Tanous07d467b2021-02-23 14:48:37 -08001167 setupPowerMatch(systemBus);
James Feist1df06a42019-04-11 14:23:04 -07001168
James Feist1b2e2242018-01-30 13:45:19 -08001169 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001170
1171 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001172}