blob: 0b519e048946f06c0e3bb5cbeecd95eb510a552e [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 Bishope45d8c72022-05-25 15:12:53 -040016/// \file entity_manager.cpp
James Feist3cb5fec2018-01-23 14:41:51 -080017
Brad Bishope45d8c72022-05-25 15:12:53 -040018#include "entity_manager.hpp"
James Feist1df06a42019-04-11 14:23:04 -070019
Brad Bishope45d8c72022-05-25 15:12:53 -040020#include "overlay.hpp"
Benjamin Fairca2eb042022-09-13 06:40:42 +000021#include "topology.hpp"
Brad Bishope45d8c72022-05-25 15:12:53 -040022#include "utils.hpp"
23#include "variant_visitors.hpp"
James Feist481c5d52019-08-13 14:40:40 -070024
James Feist11be6672018-04-06 14:05:32 -070025#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070026#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080027#include <boost/algorithm/string/predicate.hpp>
28#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070029#include <boost/algorithm/string/split.hpp>
James Feist02d2b932020-02-06 16:28:48 -080030#include <boost/asio/io_context.hpp>
Ed Tanous49a888c2023-03-06 13:44:51 -080031#include <boost/asio/post.hpp>
James Feistb1728ca2020-04-30 15:40:55 -070032#include <boost/asio/steady_timer.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080033#include <boost/container/flat_map.hpp>
34#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070035#include <boost/range/iterator_range.hpp>
James Feist8c505da2020-05-28 10:06:33 -070036#include <nlohmann/json.hpp>
37#include <sdbusplus/asio/connection.hpp>
38#include <sdbusplus/asio/object_server.hpp>
39
Igor Kononenko9fd87e52020-10-06 01:18:17 +030040#include <charconv>
James Feist637b3ef2019-04-15 16:35:30 -070041#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080042#include <fstream>
Andrew Jefferye35d0ac2022-03-24 15:53:13 +103043#include <functional>
James Feista465ccc2019-02-08 12:51:01 -080044#include <iostream>
Andrew Jeffery666583b2021-12-01 15:50:12 +103045#include <map>
James Feista465ccc2019-02-08 12:51:01 -080046#include <regex>
James Feista465ccc2019-02-08 12:51:01 -080047#include <variant>
Andrew Jefferya9c58922021-06-01 09:28:59 +093048constexpr const char* hostConfigurationDirectory = SYSCONF_DIR "configurations";
James Feista465ccc2019-02-08 12:51:01 -080049constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
50constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070051constexpr const char* tempConfigDir = "/tmp/configuration/";
52constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
53constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080054constexpr const char* globalSchema = "global.json";
James Feistf1b14142019-04-10 15:22:09 -070055
Andrew Jeffery666583b2021-12-01 15:50:12 +103056const boost::container::flat_map<const char*, probe_type_codes, CmpStr>
Ed Tanous07d467b2021-02-23 14:48:37 -080057 probeTypes{{{"FALSE", probe_type_codes::FALSE_T},
58 {"TRUE", probe_type_codes::TRUE_T},
59 {"AND", probe_type_codes::AND},
60 {"OR", probe_type_codes::OR},
61 {"FOUND", probe_type_codes::FOUND},
62 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080063
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020064static constexpr std::array<const char*, 6> settableInterfaces = {
65 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist68500ff2018-08-08 15:40:42 -070066using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080067 std::variant<std::vector<std::string>, std::vector<double>, std::string,
68 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
69 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080070
Ed Tanousfc171422024-04-04 17:18:16 -070071// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
James Feistd58879a2019-09-11 11:26:07 -070072// store reference to all interfaces so we can destroy them later
73boost::container::flat_map<
James Feist02d2b932020-02-06 16:28:48 -080074 std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
James Feistd58879a2019-09-11 11:26:07 -070075 inventory;
76
James Feist3cb5fec2018-01-23 14:41:51 -080077// todo: pass this through nicer
Ed Tanous07d467b2021-02-23 14:48:37 -080078std::shared_ptr<sdbusplus::asio::connection> systemBus;
Andrew Jeffery47af65a2021-12-01 14:16:31 +103079nlohmann::json lastJson;
Matt Spinler6eb60972023-08-14 16:36:20 -050080Topology topology;
James Feist3cb5fec2018-01-23 14:41:51 -080081
James Feist02d2b932020-02-06 16:28:48 -080082boost::asio::io_context io;
Ed Tanousfc171422024-04-04 17:18:16 -070083// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
James Feist02d2b932020-02-06 16:28:48 -080084
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
John Edward Broadbentd97c6312023-10-26 20:32:07 +000088void tryIfaceInitialize(std::shared_ptr<sdbusplus::asio::dbus_interface>& iface)
89{
90 try
91 {
92 iface->initialize();
93 }
94 catch (std::exception& e)
95 {
96 std::cerr << "Unable to initialize dbus interface : " << e.what()
97 << "\n"
98 << "object Path : " << iface->get_object_path() << "\n"
99 << "interface name : " << iface->get_interface_name() << "\n";
100 }
101}
102
Andrew Jeffery666583b2021-12-01 15:50:12 +1030103FoundProbeTypeT findProbeType(const std::string& probe)
104{
105 boost::container::flat_map<const char*, probe_type_codes,
106 CmpStr>::const_iterator probeType;
107 for (probeType = probeTypes.begin(); probeType != probeTypes.end();
108 ++probeType)
109 {
110 if (probe.find(probeType->first) != std::string::npos)
111 {
112 return probeType;
113 }
114 }
115
116 return std::nullopt;
117}
118
James Feistd58879a2019-09-11 11:26:07 -0700119static std::shared_ptr<sdbusplus::asio::dbus_interface>
120 createInterface(sdbusplus::asio::object_server& objServer,
121 const std::string& path, const std::string& interface,
James Feist02d2b932020-02-06 16:28:48 -0800122 const std::string& parent, bool checkNull = false)
James Feistd58879a2019-09-11 11:26:07 -0700123{
James Feist02d2b932020-02-06 16:28:48 -0800124 // on first add we have no reason to check for null before add, as there
125 // won't be any. For dynamically added interfaces, we check for null so that
126 // a constant delete/add will not create a memory leak
127
128 auto ptr = objServer.add_interface(path, interface);
129 auto& dataVector = inventory[parent];
130 if (checkNull)
131 {
132 auto it = std::find_if(dataVector.begin(), dataVector.end(),
133 [](const auto& p) { return p.expired(); });
134 if (it != dataVector.end())
135 {
136 *it = ptr;
137 return ptr;
138 }
139 }
140 dataVector.emplace_back(ptr);
141 return ptr;
James Feistd58879a2019-09-11 11:26:07 -0700142}
143
James Feist8f2710a2018-05-09 17:18:55 -0700144// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800145bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800146{
James Feist1df06a42019-04-11 14:23:04 -0700147 std::filesystem::create_directory(configurationOutDir);
148 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700149 if (!output.good())
150 {
151 return false;
152 }
James Feist1b2e2242018-01-30 13:45:19 -0800153 output << systemConfiguration.dump(4);
154 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700155 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700156}
James Feist1b2e2242018-01-30 13:45:19 -0800157
James Feist97a63f12018-05-17 13:50:57 -0700158template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800159bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
160 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700161{
162 try
163 {
164 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800165 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700166 ref = value;
167 return true;
168 }
James Feist98132792019-07-09 13:29:09 -0700169 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700170 {
171 return false;
172 }
173}
James Feistbb43d022018-06-12 15:44:33 -0700174
James Feistebcc26b2019-03-22 12:30:43 -0700175// template function to add array as dbus property
176template <typename PropertyType>
177void addArrayToDbus(const std::string& name, const nlohmann::json& array,
178 sdbusplus::asio::dbus_interface* iface,
179 sdbusplus::asio::PropertyPermission permission,
180 nlohmann::json& systemConfiguration,
181 const std::string& jsonPointerString)
182{
183 std::vector<PropertyType> values;
184 for (const auto& property : array)
185 {
186 auto ptr = property.get_ptr<const PropertyType*>();
187 if (ptr != nullptr)
188 {
189 values.emplace_back(*ptr);
190 }
191 }
192
193 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
194 {
195 iface->register_property(name, values);
196 }
197 else
198 {
199 iface->register_property(
200 name, values,
201 [&systemConfiguration,
202 jsonPointerString{std::string(jsonPointerString)}](
203 const std::vector<PropertyType>& newVal,
204 std::vector<PropertyType>& val) {
Patrick Williamsdf190612023-05-10 07:51:34 -0500205 val = newVal;
206 if (!setJsonFromPointer(jsonPointerString, val,
207 systemConfiguration))
208 {
209 std::cerr << "error setting json field\n";
210 return -1;
211 }
212 if (!writeJsonFiles(systemConfiguration))
213 {
214 std::cerr << "error setting json file\n";
215 return -1;
216 }
217 return 1;
Patrick Williamsb9dd7f82023-10-20 11:20:11 -0500218 });
James Feistebcc26b2019-03-22 12:30:43 -0700219 }
220}
221
James Feistbb43d022018-06-12 15:44:33 -0700222template <typename PropertyType>
Ed Tanous3013fb42022-07-09 08:27:06 -0700223void addProperty(const std::string& name, const PropertyType& value,
James Feista465ccc2019-02-08 12:51:01 -0800224 sdbusplus::asio::dbus_interface* iface,
225 nlohmann::json& systemConfiguration,
226 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700227 sdbusplus::asio::PropertyPermission permission)
228{
229 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
230 {
Ed Tanous3013fb42022-07-09 08:27:06 -0700231 iface->register_property(name, value);
James Feistbb43d022018-06-12 15:44:33 -0700232 return;
233 }
James Feist68500ff2018-08-08 15:40:42 -0700234 iface->register_property(
Ed Tanous3013fb42022-07-09 08:27:06 -0700235 name, value,
James Feist68500ff2018-08-08 15:40:42 -0700236 [&systemConfiguration,
237 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800238 const PropertyType& newVal, PropertyType& val) {
Patrick Williamsdf190612023-05-10 07:51:34 -0500239 val = newVal;
240 if (!setJsonFromPointer(jsonPointerString, val, systemConfiguration))
241 {
242 std::cerr << "error setting json field\n";
243 return -1;
244 }
245 if (!writeJsonFiles(systemConfiguration))
246 {
247 std::cerr << "error setting json file\n";
248 return -1;
249 }
250 return 1;
Patrick Williamsb9dd7f82023-10-20 11:20:11 -0500251 });
James Feistc6248a52018-08-14 10:09:45 -0700252}
253
254void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800255 const std::string& jsonPointerPath,
256 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
257 sdbusplus::asio::object_server& objServer,
258 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700259{
260 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
Patrick Williamsdf190612023-05-10 07:51:34 -0500261 iface->register_method("Delete",
262 [&objServer, &systemConfiguration, interface,
263 jsonPointerPath{std::string(jsonPointerPath)}]() {
264 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
265 interface.lock();
266 if (!dbusInterface)
267 {
268 // this technically can't happen as the pointer is pointing to
269 // us
270 throw DBusInternalError();
271 }
272 nlohmann::json::json_pointer ptr(jsonPointerPath);
273 systemConfiguration[ptr] = nullptr;
James Feistc6248a52018-08-14 10:09:45 -0700274
Patrick Williamsdf190612023-05-10 07:51:34 -0500275 // todo(james): dig through sdbusplus to find out why we can't
276 // delete it in a method call
277 boost::asio::post(io, [&objServer, dbusInterface]() mutable {
278 objServer.remove_interface(dbusInterface);
James Feist68500ff2018-08-08 15:40:42 -0700279 });
Patrick Williamsdf190612023-05-10 07:51:34 -0500280
281 if (!writeJsonFiles(systemConfiguration))
282 {
283 std::cerr << "error setting json file\n";
284 throw DBusInternalError();
285 }
286 });
James Feistbb43d022018-06-12 15:44:33 -0700287}
288
James Feist1b2e2242018-01-30 13:45:19 -0800289// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700290void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800291 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
292 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
293 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700294 sdbusplus::asio::PropertyPermission permission =
295 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800296{
Patrick Williams2594d362022-09-28 06:46:24 -0500297 for (const auto& [key, value] : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800298 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030299 auto type = value.type();
James Feist8f2710a2018-05-09 17:18:55 -0700300 bool array = false;
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030301 if (value.type() == nlohmann::json::value_t::array)
James Feist8f2710a2018-05-09 17:18:55 -0700302 {
303 array = true;
Ed Tanous3013fb42022-07-09 08:27:06 -0700304 if (value.empty())
James Feist8f2710a2018-05-09 17:18:55 -0700305 {
306 continue;
307 }
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030308 type = value[0].type();
James Feist8f2710a2018-05-09 17:18:55 -0700309 bool isLegal = true;
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030310 for (const auto& arrayItem : value)
James Feist8f2710a2018-05-09 17:18:55 -0700311 {
312 if (arrayItem.type() != type)
313 {
314 isLegal = false;
315 break;
316 }
317 }
318 if (!isLegal)
319 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030320 std::cerr << "dbus format error" << value << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700321 continue;
322 }
James Feista218ddb2019-04-11 14:01:31 -0700323 }
324 if (type == nlohmann::json::value_t::object)
325 {
326 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700327 }
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030328
329 std::string path = jsonPointerPath;
330 path.append("/").append(key);
James Feistbb43d022018-06-12 15:44:33 -0700331 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
332 {
333 // all setable numbers are doubles as it is difficult to always
334 // create a configuration file with all whole numbers as decimals
335 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700336 if (array)
337 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030338 if (value[0].is_number())
James Feistebcc26b2019-03-22 12:30:43 -0700339 {
340 type = nlohmann::json::value_t::number_float;
341 }
342 }
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030343 else if (value.is_number())
James Feistbb43d022018-06-12 15:44:33 -0700344 {
345 type = nlohmann::json::value_t::number_float;
346 }
347 }
348
James Feist8f2710a2018-05-09 17:18:55 -0700349 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800350 {
James Feist9eb0b582018-04-27 12:15:46 -0700351 case (nlohmann::json::value_t::boolean):
352 {
James Feist8f2710a2018-05-09 17:18:55 -0700353 if (array)
354 {
355 // todo: array of bool isn't detected correctly by
356 // sdbusplus, change it to numbers
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030357 addArrayToDbus<uint64_t>(key, value, iface.get(),
358 permission, systemConfiguration,
359 path);
James Feist8f2710a2018-05-09 17:18:55 -0700360 }
James Feistbb43d022018-06-12 15:44:33 -0700361
James Feist97a63f12018-05-17 13:50:57 -0700362 else
363 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030364 addProperty(key, value.get<bool>(), iface.get(),
365 systemConfiguration, path, permission);
James Feist97a63f12018-05-17 13:50:57 -0700366 }
James Feist9eb0b582018-04-27 12:15:46 -0700367 break;
368 }
369 case (nlohmann::json::value_t::number_integer):
370 {
James Feist8f2710a2018-05-09 17:18:55 -0700371 if (array)
372 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030373 addArrayToDbus<int64_t>(key, value, iface.get(), permission,
Andrew Jeffery029ee282022-03-25 13:11:36 +1030374 systemConfiguration, path);
James Feist97a63f12018-05-17 13:50:57 -0700375 }
376 else
377 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030378 addProperty(key, value.get<int64_t>(), iface.get(),
379 systemConfiguration, path,
James Feistbb43d022018-06-12 15:44:33 -0700380 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700381 }
James Feist9eb0b582018-04-27 12:15:46 -0700382 break;
383 }
384 case (nlohmann::json::value_t::number_unsigned):
385 {
James Feist8f2710a2018-05-09 17:18:55 -0700386 if (array)
387 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030388 addArrayToDbus<uint64_t>(key, value, iface.get(),
389 permission, systemConfiguration,
390 path);
James Feist97a63f12018-05-17 13:50:57 -0700391 }
392 else
393 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030394 addProperty(key, value.get<uint64_t>(), iface.get(),
Andrew Jeffery029ee282022-03-25 13:11:36 +1030395 systemConfiguration, path,
James Feistbb43d022018-06-12 15:44:33 -0700396 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700397 }
James Feist9eb0b582018-04-27 12:15:46 -0700398 break;
399 }
400 case (nlohmann::json::value_t::number_float):
401 {
James Feist8f2710a2018-05-09 17:18:55 -0700402 if (array)
403 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030404 addArrayToDbus<double>(key, value, iface.get(), permission,
Andrew Jeffery029ee282022-03-25 13:11:36 +1030405 systemConfiguration, path);
James Feist8f2710a2018-05-09 17:18:55 -0700406 }
James Feistbb43d022018-06-12 15:44:33 -0700407
James Feist97a63f12018-05-17 13:50:57 -0700408 else
409 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030410 addProperty(key, value.get<double>(), iface.get(),
411 systemConfiguration, path, permission);
James Feist97a63f12018-05-17 13:50:57 -0700412 }
James Feist9eb0b582018-04-27 12:15:46 -0700413 break;
414 }
415 case (nlohmann::json::value_t::string):
416 {
James Feist8f2710a2018-05-09 17:18:55 -0700417 if (array)
418 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030419 addArrayToDbus<std::string>(key, value, iface.get(),
420 permission, systemConfiguration,
421 path);
James Feist97a63f12018-05-17 13:50:57 -0700422 }
423 else
424 {
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030425 addProperty(key, value.get<std::string>(), iface.get(),
426 systemConfiguration, path, permission);
James Feist97a63f12018-05-17 13:50:57 -0700427 }
James Feist9eb0b582018-04-27 12:15:46 -0700428 break;
429 }
James Feist0eb40352019-04-09 14:44:04 -0700430 default:
431 {
James Feista218ddb2019-04-11 14:01:31 -0700432 std::cerr << "Unexpected json type in system configuration "
Andrew Jeffery65ea4502022-03-25 13:15:34 +1030433 << key << ": " << value.type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700434 break;
435 }
James Feist1b2e2242018-01-30 13:45:19 -0800436 }
437 }
James Feistc6248a52018-08-14 10:09:45 -0700438 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
439 {
440 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
441 systemConfiguration);
442 }
John Edward Broadbentd97c6312023-10-26 20:32:07 +0000443 tryIfaceInitialize(iface);
James Feist1b2e2242018-01-30 13:45:19 -0800444}
445
James Feista465ccc2019-02-08 12:51:01 -0800446sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700447{
448 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
449 interface) != settableInterfaces.end()
450 ? sdbusplus::asio::PropertyPermission::readWrite
451 : sdbusplus::asio::PropertyPermission::readOnly;
452}
453
James Feista465ccc2019-02-08 12:51:01 -0800454void createAddObjectMethod(const std::string& jsonPointerPath,
455 const std::string& path,
456 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700457 sdbusplus::asio::object_server& objServer,
458 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700459{
James Feistd58879a2019-09-11 11:26:07 -0700460 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
461 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700462
463 iface->register_method(
464 "AddObject",
465 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700466 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
467 board](const boost::container::flat_map<std::string, JsonVariantType>&
468 data) {
Patrick Williamsdf190612023-05-10 07:51:34 -0500469 nlohmann::json::json_pointer ptr(jsonPointerPath);
470 nlohmann::json& base = systemConfiguration[ptr];
471 auto findExposes = base.find("Exposes");
James Feist68500ff2018-08-08 15:40:42 -0700472
Patrick Williamsdf190612023-05-10 07:51:34 -0500473 if (findExposes == base.end())
474 {
475 throw std::invalid_argument("Entity must have children.");
476 }
477
478 // this will throw invalid-argument to sdbusplus if invalid json
479 nlohmann::json newData{};
480 for (const auto& item : data)
481 {
482 nlohmann::json& newJson = newData[item.first];
483 std::visit(
484 [&newJson](auto&& val) {
485 newJson = std::forward<decltype(val)>(val);
Patrick Williamsb9dd7f82023-10-20 11:20:11 -0500486 },
Patrick Williamsdf190612023-05-10 07:51:34 -0500487 item.second);
488 }
489
490 auto findName = newData.find("Name");
491 auto findType = newData.find("Type");
492 if (findName == newData.end() || findType == newData.end())
493 {
494 throw std::invalid_argument("AddObject missing Name or Type");
495 }
496 const std::string* type = findType->get_ptr<const std::string*>();
497 const std::string* name = findName->get_ptr<const std::string*>();
498 if (type == nullptr || name == nullptr)
499 {
500 throw std::invalid_argument("Type and Name must be a string.");
501 }
502
503 bool foundNull = false;
504 size_t lastIndex = 0;
505 // we add in the "exposes"
506 for (const auto& expose : *findExposes)
507 {
508 if (expose.is_null())
James Feist68500ff2018-08-08 15:40:42 -0700509 {
Patrick Williamsdf190612023-05-10 07:51:34 -0500510 foundNull = true;
511 continue;
James Feist68500ff2018-08-08 15:40:42 -0700512 }
513
Patrick Williamsdf190612023-05-10 07:51:34 -0500514 if (expose["Name"] == *name && expose["Type"] == *type)
James Feist68500ff2018-08-08 15:40:42 -0700515 {
516 throw std::invalid_argument(
Patrick Williamsdf190612023-05-10 07:51:34 -0500517 "Field already in JSON, not adding");
James Feist68500ff2018-08-08 15:40:42 -0700518 }
Patrick Williamsdf190612023-05-10 07:51:34 -0500519
James Feist02d2b932020-02-06 16:28:48 -0800520 if (foundNull)
521 {
Patrick Williamsdf190612023-05-10 07:51:34 -0500522 continue;
James Feist02d2b932020-02-06 16:28:48 -0800523 }
James Feist68500ff2018-08-08 15:40:42 -0700524
Patrick Williamsdf190612023-05-10 07:51:34 -0500525 lastIndex++;
526 }
James Feistd58879a2019-09-11 11:26:07 -0700527
Patrick Williamsdf190612023-05-10 07:51:34 -0500528 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
529 boost::to_lower_copy(*type) + ".json");
530 // todo(james) we might want to also make a list of 'can add'
531 // interfaces but for now I think the assumption if there is a
532 // schema avaliable that it is allowed to update is fine
533 if (!schemaFile.good())
534 {
535 throw std::invalid_argument(
536 "No schema avaliable, cannot validate.");
537 }
538 nlohmann::json schema = nlohmann::json::parse(schemaFile, nullptr,
Potin Lai0f3a4d92023-12-05 00:13:55 +0800539 false, true);
Patrick Williamsdf190612023-05-10 07:51:34 -0500540 if (schema.is_discarded())
541 {
542 std::cerr << "Schema not legal" << *type << ".json\n";
543 throw DBusInternalError();
544 }
545 if (!validateJson(schema, newData))
546 {
547 throw std::invalid_argument("Data does not match schema");
548 }
549 if (foundNull)
550 {
551 findExposes->at(lastIndex) = newData;
552 }
553 else
554 {
555 findExposes->push_back(newData);
556 }
557 if (!writeJsonFiles(systemConfiguration))
558 {
559 std::cerr << "Error writing json files\n";
560 throw DBusInternalError();
561 }
562 std::string dbusName = *name;
563
564 std::regex_replace(dbusName.begin(), dbusName.begin(), dbusName.end(),
565 illegalDbusMemberRegex, "_");
566
567 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
568 createInterface(objServer, path + "/" + dbusName,
569 "xyz.openbmc_project.Configuration." + *type, board,
570 true);
571 // permission is read-write, as since we just created it, must be
572 // runtime modifiable
573 populateInterfaceFromJson(
574 systemConfiguration,
575 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
576 interface, newData, objServer,
577 sdbusplus::asio::PropertyPermission::readWrite);
Patrick Williamsb9dd7f82023-10-20 11:20:11 -0500578 });
John Edward Broadbentd97c6312023-10-26 20:32:07 +0000579 tryIfaceInitialize(iface);
James Feist68500ff2018-08-08 15:40:42 -0700580}
581
James Feista465ccc2019-02-08 12:51:01 -0800582void postToDbus(const nlohmann::json& newConfiguration,
583 nlohmann::json& systemConfiguration,
584 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800585
James Feist1b2e2242018-01-30 13:45:19 -0800586{
Matt Spinler6eb60972023-08-14 16:36:20 -0500587 std::map<std::string, std::string> newBoards; // path -> name
Benjamin Fairca2eb042022-09-13 06:40:42 +0000588
James Feist97a63f12018-05-17 13:50:57 -0700589 // iterate through boards
Patrick Williams2594d362022-09-28 06:46:24 -0500590 for (const auto& [boardId, boardConfig] : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800591 {
Matt Spinler3d1909a2023-08-10 16:39:44 -0500592 std::string boardName = boardConfig["Name"];
593 std::string boardNameOrig = boardConfig["Name"];
Andrew Jeffery13132df2022-03-25 13:29:41 +1030594 std::string jsonPointerPath = "/" + boardId;
James Feist97a63f12018-05-17 13:50:57 -0700595 // loop through newConfiguration, but use values from system
596 // configuration to be able to modify via dbus later
Andrew Jeffery13132df2022-03-25 13:29:41 +1030597 auto boardValues = systemConfiguration[boardId];
James Feistd63d18a2018-07-19 15:23:45 -0700598 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800599 std::string boardType;
600 if (findBoardType != boardValues.end() &&
601 findBoardType->type() == nlohmann::json::value_t::string)
602 {
603 boardType = findBoardType->get<std::string>();
604 std::regex_replace(boardType.begin(), boardType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800605 boardType.end(), illegalDbusMemberRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800606 }
607 else
608 {
Matt Spinler3d1909a2023-08-10 16:39:44 -0500609 std::cerr << "Unable to find type for " << boardName
James Feist1b2e2242018-01-30 13:45:19 -0800610 << " reverting to Chassis.\n";
611 boardType = "Chassis";
612 }
James Feist11be6672018-04-06 14:05:32 -0700613 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800614
Matt Spinler3d1909a2023-08-10 16:39:44 -0500615 std::regex_replace(boardName.begin(), boardName.begin(),
616 boardName.end(), illegalDbusMemberRegex, "_");
617 std::string boardPath = "/xyz/openbmc_project/inventory/system/";
618 boardPath += boardtypeLower;
619 boardPath += "/";
620 boardPath += boardName;
James Feist1b2e2242018-01-30 13:45:19 -0800621
James Feistd58879a2019-09-11 11:26:07 -0700622 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
Matt Spinler3d1909a2023-08-10 16:39:44 -0500623 createInterface(objServer, boardPath,
624 "xyz.openbmc_project.Inventory.Item", boardName);
James Feist68500ff2018-08-08 15:40:42 -0700625
James Feistd58879a2019-09-11 11:26:07 -0700626 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
Matt Spinler3d1909a2023-08-10 16:39:44 -0500627 createInterface(objServer, boardPath,
James Feistd58879a2019-09-11 11:26:07 -0700628 "xyz.openbmc_project.Inventory.Item." + boardType,
Matt Spinler3d1909a2023-08-10 16:39:44 -0500629 boardNameOrig);
James Feist11be6672018-04-06 14:05:32 -0700630
Matt Spinler3d1909a2023-08-10 16:39:44 -0500631 createAddObjectMethod(jsonPointerPath, boardPath, systemConfiguration,
632 objServer, boardNameOrig);
James Feist68500ff2018-08-08 15:40:42 -0700633
James Feist97a63f12018-05-17 13:50:57 -0700634 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700635 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700636 jsonPointerPath += "/";
637 // iterate through board properties
Patrick Williams2594d362022-09-28 06:46:24 -0500638 for (const auto& [propName, propValue] : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700639 {
Andrew Jefferya96950d2022-03-25 13:32:46 +1030640 if (propValue.type() == nlohmann::json::value_t::object)
James Feist11be6672018-04-06 14:05:32 -0700641 {
James Feistd58879a2019-09-11 11:26:07 -0700642 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
Matt Spinler3d1909a2023-08-10 16:39:44 -0500643 createInterface(objServer, boardPath, propName,
644 boardNameOrig);
James Feistd58879a2019-09-11 11:26:07 -0700645
James Feistc6248a52018-08-14 10:09:45 -0700646 populateInterfaceFromJson(systemConfiguration,
Andrew Jefferya96950d2022-03-25 13:32:46 +1030647 jsonPointerPath + propName, iface,
648 propValue, objServer);
James Feist11be6672018-04-06 14:05:32 -0700649 }
650 }
James Feist97a63f12018-05-17 13:50:57 -0700651
James Feist1e3e6982018-08-03 16:09:28 -0700652 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800653 if (exposes == boardValues.end())
654 {
655 continue;
656 }
James Feist97a63f12018-05-17 13:50:57 -0700657 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700658 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700659
660 // store the board level pointer so we can modify it on the way down
661 std::string jsonPointerPathBoard = jsonPointerPath;
662 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -0800663 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -0800664 {
James Feist97a63f12018-05-17 13:50:57 -0700665 exposesIndex++;
666 jsonPointerPath = jsonPointerPathBoard;
667 jsonPointerPath += std::to_string(exposesIndex);
668
James Feistd63d18a2018-07-19 15:23:45 -0700669 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800670 if (findName == item.end())
671 {
672 std::cerr << "cannot find name in field " << item << "\n";
673 continue;
674 }
James Feist1e3e6982018-08-03 16:09:28 -0700675 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800676 // if status is not found it is assumed to be status = 'okay'
677 if (findStatus != item.end())
678 {
679 if (*findStatus == "disabled")
680 {
681 continue;
682 }
683 }
James Feistd63d18a2018-07-19 15:23:45 -0700684 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800685 std::string itemType;
686 if (findType != item.end())
687 {
688 itemType = findType->get<std::string>();
689 std::regex_replace(itemType.begin(), itemType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800690 itemType.end(), illegalDbusPathRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800691 }
692 else
693 {
694 itemType = "unknown";
695 }
696 std::string itemName = findName->get<std::string>();
697 std::regex_replace(itemName.begin(), itemName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800698 itemName.end(), illegalDbusMemberRegex, "_");
Matt Spinler3d1909a2023-08-10 16:39:44 -0500699 std::string ifacePath = boardPath;
Ed Tanous07d467b2021-02-23 14:48:37 -0800700 ifacePath += "/";
701 ifacePath += itemName;
James Feistc6248a52018-08-14 10:09:45 -0700702
James Feistd58879a2019-09-11 11:26:07 -0700703 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
Ed Tanous07d467b2021-02-23 14:48:37 -0800704 createInterface(objServer, ifacePath,
James Feistd58879a2019-09-11 11:26:07 -0700705 "xyz.openbmc_project.Configuration." + itemType,
Matt Spinler3d1909a2023-08-10 16:39:44 -0500706 boardNameOrig);
James Feist1b2e2242018-01-30 13:45:19 -0800707
Sui Chen74ebe592022-09-13 10:22:03 -0700708 if (itemType == "BMC")
709 {
710 std::shared_ptr<sdbusplus::asio::dbus_interface> bmcIface =
711 createInterface(objServer, ifacePath,
712 "xyz.openbmc_project.Inventory.Item.Bmc",
Matt Spinler3d1909a2023-08-10 16:39:44 -0500713 boardNameOrig);
Sui Chen74ebe592022-09-13 10:22:03 -0700714 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
715 bmcIface, item, objServer,
716 getPermission(itemType));
717 }
Edward Leeeb587b42023-03-08 18:59:04 +0000718 else if (itemType == "System")
719 {
720 std::shared_ptr<sdbusplus::asio::dbus_interface> systemIface =
721 createInterface(objServer, ifacePath,
722 "xyz.openbmc_project.Inventory.Item.System",
Matt Spinler3d1909a2023-08-10 16:39:44 -0500723 boardNameOrig);
Edward Leeeb587b42023-03-08 18:59:04 +0000724 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
725 systemIface, item, objServer,
726 getPermission(itemType));
727 }
Sui Chen74ebe592022-09-13 10:22:03 -0700728
James Feist97a63f12018-05-17 13:50:57 -0700729 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700730 itemIface, item, objServer,
731 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -0800732
Patrick Williams2594d362022-09-28 06:46:24 -0500733 for (const auto& [name, config] : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800734 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030735 jsonPointerPath = jsonPointerPathBoard;
736 jsonPointerPath.append(std::to_string(exposesIndex))
737 .append("/")
738 .append(name);
739 if (config.type() == nlohmann::json::value_t::object)
James Feist1b2e2242018-01-30 13:45:19 -0800740 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030741 std::string ifaceName =
742 "xyz.openbmc_project.Configuration.";
743 ifaceName.append(itemType).append(".").append(name);
James Feist97a63f12018-05-17 13:50:57 -0700744
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030745 std::shared_ptr<sdbusplus::asio::dbus_interface>
746 objectIface = createInterface(objServer, ifacePath,
Matt Spinler3d1909a2023-08-10 16:39:44 -0500747 ifaceName, boardNameOrig);
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030748
749 populateInterfaceFromJson(
750 systemConfiguration, jsonPointerPath, objectIface,
751 config, objServer, getPermission(name));
James Feist1b2e2242018-01-30 13:45:19 -0800752 }
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030753 else if (config.type() == nlohmann::json::value_t::array)
James Feist1b2e2242018-01-30 13:45:19 -0800754 {
755 size_t index = 0;
Ed Tanous3013fb42022-07-09 08:27:06 -0700756 if (config.empty())
James Feist1b2e2242018-01-30 13:45:19 -0800757 {
James Feist8f2710a2018-05-09 17:18:55 -0700758 continue;
759 }
760 bool isLegal = true;
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030761 auto type = config[0].type();
James Feist8f2710a2018-05-09 17:18:55 -0700762 if (type != nlohmann::json::value_t::object)
763 {
764 continue;
765 }
766
767 // verify legal json
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030768 for (const auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700769 {
770 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800771 {
James Feist8f2710a2018-05-09 17:18:55 -0700772 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800773 break;
774 }
James Feist8f2710a2018-05-09 17:18:55 -0700775 }
776 if (!isLegal)
777 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030778 std::cerr << "dbus format error" << config << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700779 break;
780 }
781
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030782 for (auto& arrayItem : config)
James Feist8f2710a2018-05-09 17:18:55 -0700783 {
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030784 std::string ifaceName =
785 "xyz.openbmc_project.Configuration.";
786 ifaceName.append(itemType).append(".").append(name);
787 ifaceName.append(std::to_string(index));
James Feist97a63f12018-05-17 13:50:57 -0700788
James Feistd58879a2019-09-11 11:26:07 -0700789 std::shared_ptr<sdbusplus::asio::dbus_interface>
790 objectIface = createInterface(
Matt Spinler3d1909a2023-08-10 16:39:44 -0500791 objServer, ifacePath, ifaceName, boardNameOrig);
James Feistd58879a2019-09-11 11:26:07 -0700792
James Feistc6248a52018-08-14 10:09:45 -0700793 populateInterfaceFromJson(
794 systemConfiguration,
795 jsonPointerPath + "/" + std::to_string(index),
796 objectIface, arrayItem, objServer,
Andrew Jeffery5a6379c2022-03-25 13:25:31 +1030797 getPermission(name));
James Feistbb43d022018-06-12 15:44:33 -0700798 index++;
James Feist1b2e2242018-01-30 13:45:19 -0800799 }
800 }
801 }
Benjamin Fairca2eb042022-09-13 06:40:42 +0000802
Matt Spinler6eb60972023-08-14 16:36:20 -0500803 topology.addBoard(boardPath, boardType, boardNameOrig, item);
James Feist1b2e2242018-01-30 13:45:19 -0800804 }
Matt Spinler6eb60972023-08-14 16:36:20 -0500805
806 newBoards.emplace(boardPath, boardNameOrig);
James Feist1b2e2242018-01-30 13:45:19 -0800807 }
Benjamin Fairca2eb042022-09-13 06:40:42 +0000808
Matt Spinler6eb60972023-08-14 16:36:20 -0500809 for (const auto& [assocPath, assocPropValue] :
810 topology.getAssocs(newBoards))
Benjamin Fairca2eb042022-09-13 06:40:42 +0000811 {
Matt Spinler6eb60972023-08-14 16:36:20 -0500812 auto findBoard = newBoards.find(assocPath);
813 if (findBoard == newBoards.end())
814 {
815 continue;
816 }
Benjamin Fairca2eb042022-09-13 06:40:42 +0000817
Matt Spinler6eb60972023-08-14 16:36:20 -0500818 auto ifacePtr = createInterface(
819 objServer, assocPath, "xyz.openbmc_project.Association.Definitions",
820 findBoard->second);
821
822 ifacePtr->register_property("Associations", assocPropValue);
John Edward Broadbentd97c6312023-10-26 20:32:07 +0000823 tryIfaceInitialize(ifacePtr);
Benjamin Fairca2eb042022-09-13 06:40:42 +0000824 }
James Feist1b2e2242018-01-30 13:45:19 -0800825}
826
James Feist8f2710a2018-05-09 17:18:55 -0700827// reads json files out of the filesystem
Andrew Jefferyf3311792022-03-29 22:09:00 +1030828bool loadConfigurations(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -0800829{
830 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -0800831 std::vector<std::filesystem::path> jsonPaths;
Andrew Jefferya9c58922021-06-01 09:28:59 +0930832 if (!findFiles(
833 std::vector<std::filesystem::path>{configurationDirectory,
834 hostConfigurationDirectory},
835 R"(.*\.json)", jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -0800836 {
837 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -0700838 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -0800839 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800840 }
James Feistb4383f42018-08-06 16:54:10 -0700841
842 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
843 globalSchema);
844 if (!schemaStream.good())
845 {
846 std::cerr
847 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
848 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -0800849 return false;
James Feistb4383f42018-08-06 16:54:10 -0700850 }
Potin Lai0f3a4d92023-12-05 00:13:55 +0800851 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false,
852 true);
James Feistb4383f42018-08-06 16:54:10 -0700853 if (schema.is_discarded())
854 {
855 std::cerr
856 << "Illegal schema file detected, cannot validate JSON, exiting\n";
857 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -0800858 return false;
James Feistb4383f42018-08-06 16:54:10 -0700859 }
860
James Feista465ccc2019-02-08 12:51:01 -0800861 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -0800862 {
863 std::ifstream jsonStream(jsonPath.c_str());
864 if (!jsonStream.good())
865 {
866 std::cerr << "unable to open " << jsonPath.string() << "\n";
867 continue;
868 }
Potin Lai0f3a4d92023-12-05 00:13:55 +0800869 auto data = nlohmann::json::parse(jsonStream, nullptr, false, true);
James Feist3cb5fec2018-01-23 14:41:51 -0800870 if (data.is_discarded())
871 {
872 std::cerr << "syntax error in " << jsonPath.string() << "\n";
873 continue;
874 }
James Feist8da99192019-01-24 08:20:16 -0800875 /*
876 * todo(james): reenable this once less things are in flight
877 *
James Feistb4383f42018-08-06 16:54:10 -0700878 if (!validateJson(schema, data))
879 {
880 std::cerr << "Error validating " << jsonPath.string() << "\n";
881 continue;
882 }
James Feist8da99192019-01-24 08:20:16 -0800883 */
James Feistb4383f42018-08-06 16:54:10 -0700884
James Feist3cb5fec2018-01-23 14:41:51 -0800885 if (data.type() == nlohmann::json::value_t::array)
886 {
James Feista465ccc2019-02-08 12:51:01 -0800887 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -0800888 {
889 configurations.emplace_back(d);
890 }
891 }
892 else
893 {
894 configurations.emplace_back(data);
895 }
896 }
Ed Tanous072e25d2018-12-16 21:45:20 -0800897 return true;
James Feist75fdeeb2018-02-20 14:26:16 -0800898}
James Feist3cb5fec2018-01-23 14:41:51 -0800899
Andrew Jeffery55192932022-03-24 12:29:27 +1030900static bool deviceRequiresPowerOn(const nlohmann::json& entity)
901{
902 auto powerState = entity.find("PowerState");
Andrew Jefferyb6209442022-03-24 12:36:20 +1030903 if (powerState == entity.end())
Andrew Jeffery55192932022-03-24 12:29:27 +1030904 {
Andrew Jefferyb6209442022-03-24 12:36:20 +1030905 return false;
Andrew Jeffery55192932022-03-24 12:29:27 +1030906 }
907
Ed Tanous3013fb42022-07-09 08:27:06 -0700908 const auto* ptr = powerState->get_ptr<const std::string*>();
909 if (ptr == nullptr)
Andrew Jefferyb6209442022-03-24 12:36:20 +1030910 {
911 return false;
912 }
913
914 return *ptr == "On" || *ptr == "BiosPost";
Andrew Jeffery55192932022-03-24 12:29:27 +1030915}
916
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030917static void pruneDevice(const nlohmann::json& systemConfiguration,
918 const bool powerOff, const bool scannedPowerOff,
919 const std::string& name, const nlohmann::json& device)
920{
921 if (systemConfiguration.contains(name))
922 {
923 return;
924 }
925
Andrew Jeffery4db38bc2022-03-24 13:42:41 +1030926 if (deviceRequiresPowerOn(device) && (powerOff || scannedPowerOff))
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030927 {
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030928 return;
929 }
930
931 logDeviceRemoved(device);
932}
933
James Feistb1728ca2020-04-30 15:40:55 -0700934void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -0700935 nlohmann::json& systemConfiguration)
936{
937 static bool scannedPowerOff = false;
938 static bool scannedPowerOn = false;
939
James Feistfb00f392019-06-25 14:16:48 -0700940 if (systemConfiguration.empty() || lastJson.empty())
941 {
942 return; // not ready yet
943 }
James Feist1df06a42019-04-11 14:23:04 -0700944 if (scannedPowerOn)
945 {
946 return;
947 }
948
949 if (!isPowerOn() && scannedPowerOff)
950 {
951 return;
952 }
953
James Feistb1728ca2020-04-30 15:40:55 -0700954 timer.expires_after(std::chrono::seconds(10));
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030955 timer.async_wait(
956 [&systemConfiguration](const boost::system::error_code& ec) {
Patrick Williamsdf190612023-05-10 07:51:34 -0500957 if (ec == boost::asio::error::operation_aborted)
958 {
959 return;
960 }
Andrew Jeffery27a1cfb2022-03-24 12:31:53 +1030961
Patrick Williamsdf190612023-05-10 07:51:34 -0500962 bool powerOff = !isPowerOn();
963 for (const auto& [name, device] : lastJson.items())
964 {
965 pruneDevice(systemConfiguration, powerOff, scannedPowerOff, name,
966 device);
967 }
Andrew Jeffery89ec3522022-03-24 13:30:41 +1030968
Patrick Williamsdf190612023-05-10 07:51:34 -0500969 scannedPowerOff = true;
970 if (!powerOff)
971 {
972 scannedPowerOn = true;
973 }
974 });
James Feist1df06a42019-04-11 14:23:04 -0700975}
976
Andrew Jeffery2f750d22022-03-24 14:32:57 +1030977static std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
978 getDeviceInterfaces(const nlohmann::json& device)
979{
980 return inventory[device["Name"].get<std::string>()];
981}
982
Andrew Jeffery0d0c1462022-03-24 15:26:39 +1030983static void pruneConfiguration(nlohmann::json& systemConfiguration,
984 sdbusplus::asio::object_server& objServer,
985 bool powerOff, const std::string& name,
986 const nlohmann::json& device)
987{
988 if (powerOff && deviceRequiresPowerOn(device))
989 {
990 // power not on yet, don't know if it's there or not
991 return;
992 }
993
994 auto& ifaces = getDeviceInterfaces(device);
995 for (auto& iface : ifaces)
996 {
997 auto sharedPtr = iface.lock();
998 if (!!sharedPtr)
999 {
1000 objServer.remove_interface(sharedPtr);
1001 }
1002 }
1003
1004 ifaces.clear();
1005 systemConfiguration.erase(name);
Matt Spinler6eb60972023-08-14 16:36:20 -05001006 topology.remove(device["Name"].get<std::string>());
Andrew Jeffery0d0c1462022-03-24 15:26:39 +10301007 logDeviceRemoved(device);
1008}
1009
Andrew Jeffery7c4cbbe2022-03-24 15:27:10 +10301010static void deriveNewConfiguration(const nlohmann::json& oldConfiguration,
1011 nlohmann::json& newConfiguration)
1012{
1013 for (auto it = newConfiguration.begin(); it != newConfiguration.end();)
1014 {
1015 auto findKey = oldConfiguration.find(it.key());
1016 if (findKey != oldConfiguration.end())
1017 {
1018 it = newConfiguration.erase(it);
1019 }
1020 else
1021 {
1022 it++;
1023 }
1024 }
1025}
1026
Andrew Jefferye35d0ac2022-03-24 15:53:13 +10301027static void publishNewConfiguration(
1028 const size_t& instance, const size_t count,
1029 boost::asio::steady_timer& timer, nlohmann::json& systemConfiguration,
1030 // Gerrit discussion:
1031 // https://gerrit.openbmc-project.xyz/c/openbmc/entity-manager/+/52316/6
1032 //
1033 // Discord discussion:
1034 // https://discord.com/channels/775381525260664832/867820390406422538/958048437729910854
1035 //
1036 // NOLINTNEXTLINE(performance-unnecessary-value-param)
1037 const nlohmann::json newConfiguration,
1038 sdbusplus::asio::object_server& objServer)
1039{
1040 loadOverlays(newConfiguration);
1041
Ed Tanous49a888c2023-03-06 13:44:51 -08001042 boost::asio::post(io, [systemConfiguration]() {
Andrew Jefferye35d0ac2022-03-24 15:53:13 +10301043 if (!writeJsonFiles(systemConfiguration))
1044 {
1045 std::cerr << "Error writing json files\n";
1046 }
1047 });
1048
Ed Tanous49a888c2023-03-06 13:44:51 -08001049 boost::asio::post(io, [&instance, count, &timer, newConfiguration,
1050 &systemConfiguration, &objServer]() {
Andrew Jefferye35d0ac2022-03-24 15:53:13 +10301051 postToDbus(newConfiguration, systemConfiguration, objServer);
1052 if (count == instance)
1053 {
1054 startRemovedTimer(timer, systemConfiguration);
1055 }
1056 });
1057}
1058
James Feist8f2710a2018-05-09 17:18:55 -07001059// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -07001060void propertiesChangedCallback(nlohmann::json& systemConfiguration,
1061 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001062{
James Feist2539ccd2020-05-01 16:15:08 -07001063 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -07001064 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001065 static size_t instance = 0;
1066 instance++;
1067 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001068
James Feistb1728ca2020-04-30 15:40:55 -07001069 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001070
1071 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -07001072 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -07001073 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001074 if (ec == boost::asio::error::operation_aborted)
1075 {
1076 // we were cancelled
1077 return;
1078 }
Ed Tanous07d467b2021-02-23 14:48:37 -08001079 if (ec)
James Feist8f2710a2018-05-09 17:18:55 -07001080 {
1081 std::cerr << "async wait error " << ec << "\n";
1082 return;
1083 }
1084
James Feist2539ccd2020-05-01 16:15:08 -07001085 if (inProgress)
1086 {
1087 propertiesChangedCallback(systemConfiguration, objServer);
1088 return;
1089 }
1090 inProgress = true;
1091
James Feist8f2710a2018-05-09 17:18:55 -07001092 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001093 auto missingConfigurations = std::make_shared<nlohmann::json>();
1094 *missingConfigurations = systemConfiguration;
1095
James Feist8f2710a2018-05-09 17:18:55 -07001096 std::list<nlohmann::json> configurations;
Andrew Jefferyf3311792022-03-29 22:09:00 +10301097 if (!loadConfigurations(configurations))
James Feist8f2710a2018-05-09 17:18:55 -07001098 {
Andrew Jefferyf3311792022-03-29 22:09:00 +10301099 std::cerr << "Could not load configurations\n";
James Feist2539ccd2020-05-01 16:15:08 -07001100 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -07001101 return;
1102 }
1103
1104 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001105 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001106 objServer,
1107 [&systemConfiguration, &objServer, count, oldConfiguration,
1108 missingConfigurations]() {
Patrick Williamsdf190612023-05-10 07:51:34 -05001109 // this is something that since ac has been applied to the bmc
1110 // we saw, and we no longer see it
1111 bool powerOff = !isPowerOn();
1112 for (const auto& [name, device] : missingConfigurations->items())
1113 {
1114 pruneConfiguration(systemConfiguration, objServer, powerOff,
1115 name, device);
1116 }
James Feist899e17f2019-09-13 11:46:29 -07001117
Patrick Williamsdf190612023-05-10 07:51:34 -05001118 nlohmann::json newConfiguration = systemConfiguration;
Andrew Jeffery7c4cbbe2022-03-24 15:27:10 +10301119
Patrick Williamsdf190612023-05-10 07:51:34 -05001120 deriveNewConfiguration(oldConfiguration, newConfiguration);
Andrew Jeffery7c4cbbe2022-03-24 15:27:10 +10301121
Patrick Williamsdf190612023-05-10 07:51:34 -05001122 for (const auto& [_, device] : newConfiguration.items())
1123 {
1124 logDeviceAdded(device);
1125 }
James Feist899e17f2019-09-13 11:46:29 -07001126
Patrick Williamsdf190612023-05-10 07:51:34 -05001127 inProgress = false;
James Feist2539ccd2020-05-01 16:15:08 -07001128
Patrick Williamsdf190612023-05-10 07:51:34 -05001129 boost::asio::post(
1130 io, std::bind_front(publishNewConfiguration, std::ref(instance),
1131 count, std::ref(timer),
1132 std::ref(systemConfiguration),
1133 newConfiguration, std::ref(objServer)));
Patrick Williamsb9dd7f82023-10-20 11:20:11 -05001134 });
James Feist8f2710a2018-05-09 17:18:55 -07001135 perfScan->run();
1136 });
James Feist75fdeeb2018-02-20 14:26:16 -08001137}
1138
Matt Spinler8d1ac3a2023-02-02 09:48:04 -06001139// Extract the D-Bus interfaces to probe from the JSON config files.
1140static std::set<std::string> getProbeInterfaces()
1141{
1142 std::set<std::string> interfaces;
1143 std::list<nlohmann::json> configurations;
1144 if (!loadConfigurations(configurations))
1145 {
1146 return interfaces;
1147 }
1148
1149 for (auto it = configurations.begin(); it != configurations.end();)
1150 {
1151 auto findProbe = it->find("Probe");
1152 if (findProbe == it->end())
1153 {
1154 std::cerr << "configuration file missing probe:\n " << *it << "\n";
1155 it++;
1156 continue;
1157 }
1158
1159 nlohmann::json probeCommand;
1160 if ((*findProbe).type() != nlohmann::json::value_t::array)
1161 {
1162 probeCommand = nlohmann::json::array();
1163 probeCommand.push_back(*findProbe);
1164 }
1165 else
1166 {
1167 probeCommand = *findProbe;
1168 }
1169
1170 for (const nlohmann::json& probeJson : probeCommand)
1171 {
1172 const std::string* probe = probeJson.get_ptr<const std::string*>();
1173 if (probe == nullptr)
1174 {
1175 std::cerr << "Probe statement wasn't a string, can't parse";
1176 continue;
1177 }
1178 // Skip it if the probe cmd doesn't contain an interface.
1179 if (findProbeType(*probe))
1180 {
1181 continue;
1182 }
1183
1184 // syntax requires probe before first open brace
1185 auto findStart = probe->find('(');
1186 if (findStart != std::string::npos)
1187 {
1188 std::string interface = probe->substr(0, findStart);
1189 interfaces.emplace(interface);
1190 }
1191 }
1192 it++;
1193 }
1194
1195 return interfaces;
1196}
1197
1198// Check if InterfacesAdded payload contains an iface that needs probing.
1199static bool
1200 iaContainsProbeInterface(sdbusplus::message_t& msg,
1201 const std::set<std::string>& probeInterfaces)
1202{
1203 sdbusplus::message::object_path path;
1204 DBusObject interfaces;
1205 std::set<std::string> interfaceSet;
1206 std::set<std::string> intersect;
1207
1208 msg.read(path, interfaces);
1209
1210 std::for_each(interfaces.begin(), interfaces.end(),
1211 [&interfaceSet](const auto& iface) {
Patrick Williamsdf190612023-05-10 07:51:34 -05001212 interfaceSet.insert(iface.first);
1213 });
Matt Spinler8d1ac3a2023-02-02 09:48:04 -06001214
1215 std::set_intersection(interfaceSet.begin(), interfaceSet.end(),
1216 probeInterfaces.begin(), probeInterfaces.end(),
1217 std::inserter(intersect, intersect.end()));
1218 return !intersect.empty();
1219}
1220
1221// Check if InterfacesRemoved payload contains an iface that needs probing.
1222static bool
1223 irContainsProbeInterface(sdbusplus::message_t& msg,
1224 const std::set<std::string>& probeInterfaces)
1225{
1226 sdbusplus::message::object_path path;
1227 std::set<std::string> interfaces;
1228 std::set<std::string> intersect;
1229
1230 msg.read(path, interfaces);
1231
1232 std::set_intersection(interfaces.begin(), interfaces.end(),
1233 probeInterfaces.begin(), probeInterfaces.end(),
1234 std::inserter(intersect, intersect.end()));
1235 return !intersect.empty();
1236}
1237
James Feist98132792019-07-09 13:29:09 -07001238int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001239{
1240 // setup connection to dbus
Ed Tanous07d467b2021-02-23 14:48:37 -08001241 systemBus = std::make_shared<sdbusplus::asio::connection>(io);
1242 systemBus->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001243
Nan Zhoua3315672022-09-20 19:48:14 +00001244 // The EntityManager object itself doesn't expose any properties.
1245 // No need to set up ObjectManager for the |EntityManager| object.
1246 sdbusplus::asio::object_server objServer(systemBus, /*skipManager=*/true);
1247
1248 // All other objects that EntityManager currently support are under the
1249 // inventory subtree.
1250 // See the discussion at
1251 // https://discord.com/channels/775381525260664832/1018929092009144380
1252 objServer.add_manager("/xyz/openbmc_project/inventory");
James Feistfd1264a2018-05-03 12:10:00 -07001253
James Feist8f2710a2018-05-09 17:18:55 -07001254 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1255 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1256 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001257
James Feist4131aea2018-03-09 09:47:30 -08001258 // to keep reference to the match / filter objects so they don't get
1259 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001260
1261 nlohmann::json systemConfiguration = nlohmann::json::object();
1262
Matt Spinler8d1ac3a2023-02-02 09:48:04 -06001263 std::set<std::string> probeInterfaces = getProbeInterfaces();
1264
Brad Bishopc76af0f2020-12-04 13:50:23 -05001265 // We need a poke from DBus for static providers that create all their
1266 // objects prior to claiming a well-known name, and thus don't emit any
1267 // org.freedesktop.DBus.Properties signals. Similarly if a process exits
1268 // for any reason, expected or otherwise, we'll need a poke to remove
1269 // entities from DBus.
Patrick Williams2af39222022-07-22 19:26:56 -05001270 sdbusplus::bus::match_t nameOwnerChangedMatch(
1271 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishopc76af0f2020-12-04 13:50:23 -05001272 sdbusplus::bus::match::rules::nameOwnerChanged(),
Patrick Williams7b8786f2022-10-10 10:23:37 -05001273 [&](sdbusplus::message_t& m) {
Patrick Williamsdf190612023-05-10 07:51:34 -05001274 auto [name, oldOwner,
1275 newOwner] = m.unpack<std::string, std::string, std::string>();
Patrick Williams7b8786f2022-10-10 10:23:37 -05001276
Patrick Williamsdf190612023-05-10 07:51:34 -05001277 if (name.starts_with(':'))
1278 {
1279 // We should do nothing with unique-name connections.
1280 return;
1281 }
Patrick Williams7b8786f2022-10-10 10:23:37 -05001282
Patrick Williamsdf190612023-05-10 07:51:34 -05001283 propertiesChangedCallback(systemConfiguration, objServer);
Patrick Williamsb9dd7f82023-10-20 11:20:11 -05001284 });
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001285 // We also need a poke from DBus when new interfaces are created or
1286 // destroyed.
Patrick Williams2af39222022-07-22 19:26:56 -05001287 sdbusplus::bus::match_t interfacesAddedMatch(
1288 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001289 sdbusplus::bus::match::rules::interfacesAdded(),
Matt Spinler8d1ac3a2023-02-02 09:48:04 -06001290 [&](sdbusplus::message_t& msg) {
Patrick Williamsdf190612023-05-10 07:51:34 -05001291 if (iaContainsProbeInterface(msg, probeInterfaces))
1292 {
1293 propertiesChangedCallback(systemConfiguration, objServer);
1294 }
Patrick Williamsb9dd7f82023-10-20 11:20:11 -05001295 });
Patrick Williams2af39222022-07-22 19:26:56 -05001296 sdbusplus::bus::match_t interfacesRemovedMatch(
1297 static_cast<sdbusplus::bus_t&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001298 sdbusplus::bus::match::rules::interfacesRemoved(),
Matt Spinler8d1ac3a2023-02-02 09:48:04 -06001299 [&](sdbusplus::message_t& msg) {
Patrick Williamsdf190612023-05-10 07:51:34 -05001300 if (irContainsProbeInterface(msg, probeInterfaces))
1301 {
1302 propertiesChangedCallback(systemConfiguration, objServer);
1303 }
Patrick Williamsb9dd7f82023-10-20 11:20:11 -05001304 });
Brad Bishopc76af0f2020-12-04 13:50:23 -05001305
Ed Tanous49a888c2023-03-06 13:44:51 -08001306 boost::asio::post(io, [&]() {
1307 propertiesChangedCallback(systemConfiguration, objServer);
1308 });
James Feist4131aea2018-03-09 09:47:30 -08001309
James Feistfd1264a2018-05-03 12:10:00 -07001310 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001311 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001312 });
John Edward Broadbentd97c6312023-10-26 20:32:07 +00001313 tryIfaceInitialize(entityIface);
James Feist8f2710a2018-05-09 17:18:55 -07001314
James Feist1df06a42019-04-11 14:23:04 -07001315 if (fwVersionIsSame())
1316 {
1317 if (std::filesystem::is_regular_file(currentConfiguration))
1318 {
1319 // this file could just be deleted, but it's nice for debug
1320 std::filesystem::create_directory(tempConfigDir);
1321 std::filesystem::remove(lastConfiguration);
1322 std::filesystem::copy(currentConfiguration, lastConfiguration);
1323 std::filesystem::remove(currentConfiguration);
1324
1325 std::ifstream jsonStream(lastConfiguration);
1326 if (jsonStream.good())
1327 {
1328 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1329 if (data.is_discarded())
1330 {
1331 std::cerr << "syntax error in " << lastConfiguration
1332 << "\n";
1333 }
1334 else
1335 {
1336 lastJson = std::move(data);
1337 }
1338 }
1339 else
1340 {
1341 std::cerr << "unable to open " << lastConfiguration << "\n";
1342 }
1343 }
1344 }
1345 else
1346 {
1347 // not an error, just logging at this level to make it in the journal
1348 std::cerr << "Clearing previous configuration\n";
1349 std::filesystem::remove(currentConfiguration);
1350 }
1351
1352 // some boards only show up after power is on, we want to not say they are
1353 // removed until the same state happens
Ed Tanous07d467b2021-02-23 14:48:37 -08001354 setupPowerMatch(systemBus);
James Feist1df06a42019-04-11 14:23:04 -07001355
James Feist1b2e2242018-01-30 13:45:19 -08001356 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001357
1358 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001359}