blob: 038bd417e7bc78382c8d66d086ffa2b8bf2dd370 [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*/
16
James Feist1df06a42019-04-11 14:23:04 -070017#include "EntityManager.hpp"
18
Patrick Venturea49dc332019-10-26 08:32:02 -070019#include "Overlay.hpp"
20#include "Utils.hpp"
James Feist481c5d52019-08-13 14:40:40 -070021#include "VariantVisitors.hpp"
22
James Feist11be6672018-04-06 14:05:32 -070023#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070024#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080025#include <boost/algorithm/string/predicate.hpp>
26#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070027#include <boost/algorithm/string/split.hpp>
James Feist02d2b932020-02-06 16:28:48 -080028#include <boost/asio/io_context.hpp>
James Feistb1728ca2020-04-30 15:40:55 -070029#include <boost/asio/steady_timer.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080030#include <boost/container/flat_map.hpp>
31#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070032#include <boost/range/iterator_range.hpp>
James Feist8c505da2020-05-28 10:06:33 -070033#include <nlohmann/json.hpp>
34#include <sdbusplus/asio/connection.hpp>
35#include <sdbusplus/asio/object_server.hpp>
36
James Feist637b3ef2019-04-15 16:35:30 -070037#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080038#include <fstream>
39#include <iostream>
James Feista465ccc2019-02-08 12:51:01 -080040#include <regex>
James Feista465ccc2019-02-08 12:51:01 -080041#include <variant>
James Feista465ccc2019-02-08 12:51:01 -080042constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
43constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070044constexpr const char* tempConfigDir = "/tmp/configuration/";
45constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
46constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080047constexpr const char* globalSchema = "global.json";
James Feist8f2710a2018-05-09 17:18:55 -070048constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080049
James Feistf1b14142019-04-10 15:22:09 -070050constexpr const bool DEBUG = false;
51
James Feist3cb5fec2018-01-23 14:41:51 -080052struct cmp_str
53{
James Feista465ccc2019-02-08 12:51:01 -080054 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080055 {
56 return std::strcmp(a, b) < 0;
57 }
58};
59
60// underscore T for collison with dbus c api
61enum class probe_type_codes
62{
63 FALSE_T,
64 TRUE_T,
65 AND,
66 OR,
James Feist6bd2a022018-03-13 12:30:58 -070067 FOUND,
68 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080069};
James Feista465ccc2019-02-08 12:51:01 -080070const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080071 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
72 {"TRUE", probe_type_codes::TRUE_T},
73 {"AND", probe_type_codes::AND},
74 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070075 {"FOUND", probe_type_codes::FOUND},
76 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080077
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020078static constexpr std::array<const char*, 6> settableInterfaces = {
79 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist68500ff2018-08-08 15:40:42 -070080using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080081 std::variant<std::vector<std::string>, std::vector<double>, std::string,
82 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
83 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080084using GetSubTreeType = std::vector<
85 std::pair<std::string,
86 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
87
88using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070089 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080090 boost::container::flat_map<
91 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070092 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080093
James Feistd58879a2019-09-11 11:26:07 -070094// store reference to all interfaces so we can destroy them later
95boost::container::flat_map<
James Feist02d2b932020-02-06 16:28:48 -080096 std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
James Feistd58879a2019-09-11 11:26:07 -070097 inventory;
98
James Feist3cb5fec2018-01-23 14:41:51 -080099// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700100std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -0700101static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800102
James Feist02d2b932020-02-06 16:28:48 -0800103boost::asio::io_context io;
104
Johnathan Mantey2015f752019-03-26 15:22:31 -0700105const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
106const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800107
James Feist4dc617b2020-05-01 09:54:47 -0700108void registerCallback(nlohmann::json& systemConfiguration,
109 sdbusplus::asio::object_server& objServer,
110 const std::string& interfaces);
James Feist75fdeeb2018-02-20 14:26:16 -0800111
James Feistd58879a2019-09-11 11:26:07 -0700112static std::shared_ptr<sdbusplus::asio::dbus_interface>
113 createInterface(sdbusplus::asio::object_server& objServer,
114 const std::string& path, const std::string& interface,
James Feist02d2b932020-02-06 16:28:48 -0800115 const std::string& parent, bool checkNull = false)
James Feistd58879a2019-09-11 11:26:07 -0700116{
James Feist02d2b932020-02-06 16:28:48 -0800117 // on first add we have no reason to check for null before add, as there
118 // won't be any. For dynamically added interfaces, we check for null so that
119 // a constant delete/add will not create a memory leak
120
121 auto ptr = objServer.add_interface(path, interface);
122 auto& dataVector = inventory[parent];
123 if (checkNull)
124 {
125 auto it = std::find_if(dataVector.begin(), dataVector.end(),
126 [](const auto& p) { return p.expired(); });
127 if (it != dataVector.end())
128 {
129 *it = ptr;
130 return ptr;
131 }
132 }
133 dataVector.emplace_back(ptr);
134 return ptr;
James Feistd58879a2019-09-11 11:26:07 -0700135}
136
James Feistb1728ca2020-04-30 15:40:55 -0700137void getInterfaces(
138 const std::tuple<std::string, std::string, std::string>& call,
139 const std::vector<std::shared_ptr<PerformProbe>>& probeVector,
140 std::shared_ptr<PerformScan> scan, size_t retries = 5)
141{
142 if (!retries)
143 {
144 std::cerr << "retries exhausted on " << std::get<0>(call) << " "
145 << std::get<1>(call) << " " << std::get<2>(call) << "\n";
146 return;
147 }
148
149 SYSTEM_BUS->async_method_call(
150 [call, scan, probeVector, retries](
151 boost::system::error_code& errc,
152 const boost::container::flat_map<std::string, BasicVariantType>&
153 resp) {
154 if (errc)
155 {
156 std::cerr << "error calling getall on " << std::get<0>(call)
157 << " " << std::get<1>(call) << " "
158 << std::get<2>(call) << "\n";
159
160 std::shared_ptr<boost::asio::steady_timer> timer =
161 std::make_shared<boost::asio::steady_timer>(io);
162 timer->expires_after(std::chrono::seconds(2));
163
164 timer->async_wait([timer, call, scan, probeVector,
165 retries](const boost::system::error_code&) {
166 getInterfaces(call, probeVector, scan, retries - 1);
167 });
168 return;
169 }
170
171 scan->dbusProbeObjects[std::get<2>(call)].emplace_back(resp);
172 },
173 std::get<0>(call), std::get<1>(call), "org.freedesktop.DBus.Properties",
174 "GetAll", std::get<2>(call));
175
176 if constexpr (DEBUG)
177 {
178 std::cerr << __func__ << " " << __LINE__ << "\n";
179 }
180}
181
James Feist3cb5fec2018-01-23 14:41:51 -0800182// calls the mapper to find all exposed objects of an interface type
183// and creates a vector<flat_map> that contains all the key value pairs
184// getManagedObjects
James Feistb1728ca2020-04-30 15:40:55 -0700185void findDbusObjects(std::vector<std::shared_ptr<PerformProbe>>&& probeVector,
186 boost::container::flat_set<std::string>&& interfaces,
James Feist733f7652019-11-13 14:32:29 -0800187 std::shared_ptr<PerformScan> scan)
James Feist3cb5fec2018-01-23 14:41:51 -0800188{
James Feist8f2710a2018-05-09 17:18:55 -0700189
James Feist733f7652019-11-13 14:32:29 -0800190 for (const auto& [interface, _] : scan->dbusProbeObjects)
191 {
192 interfaces.erase(interface);
193 }
194 if (interfaces.empty())
195 {
196 return;
197 }
198
James Feist3cb5fec2018-01-23 14:41:51 -0800199 // find all connections in the mapper that expose a specific type
James Feistb1728ca2020-04-30 15:40:55 -0700200 SYSTEM_BUS->async_method_call(
201 [interfaces{std::move(interfaces)}, probeVector{std::move(probeVector)},
James Feist733f7652019-11-13 14:32:29 -0800202 scan](boost::system::error_code& ec,
203 const GetSubTreeType& interfaceSubtree) {
James Feistb1728ca2020-04-30 15:40:55 -0700204 boost::container::flat_set<
205 std::tuple<std::string, std::string, std::string>>
206 interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700207 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700208 {
James Feist0de40152018-07-25 11:56:12 -0700209 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700210 {
James Feist0de40152018-07-25 11:56:12 -0700211 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700212 }
James Feist0de40152018-07-25 11:56:12 -0700213 std::cerr << "Error communicating to mapper.\n";
214
215 // if we can't communicate to the mapper something is very wrong
216 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700217 }
James Feist787c3c32019-11-07 14:42:58 -0800218
James Feistb1728ca2020-04-30 15:40:55 -0700219 for (const auto& [path, object] : interfaceSubtree)
James Feist3cb5fec2018-01-23 14:41:51 -0800220 {
James Feistb1728ca2020-04-30 15:40:55 -0700221 for (const auto& [busname, ifaces] : object)
James Feist8f2710a2018-05-09 17:18:55 -0700222 {
James Feistb1728ca2020-04-30 15:40:55 -0700223 for (const std::string& iface : ifaces)
224 {
225 auto ifaceObjFind = interfaces.find(iface);
226
227 if (ifaceObjFind != interfaces.end())
228 {
229 interfaceConnections.emplace(busname, path, iface);
230 }
231 }
James Feist8f2710a2018-05-09 17:18:55 -0700232 }
James Feist3cb5fec2018-01-23 14:41:51 -0800233 }
James Feist787c3c32019-11-07 14:42:58 -0800234
James Feist63845bf2019-01-24 12:19:51 -0800235 if (interfaceConnections.empty())
236 {
James Feist63845bf2019-01-24 12:19:51 -0800237 return;
238 }
James Feist787c3c32019-11-07 14:42:58 -0800239
James Feistb1728ca2020-04-30 15:40:55 -0700240 for (const auto& call : interfaceConnections)
241 {
242 getInterfaces(call, probeVector, scan);
James Feist8f2710a2018-05-09 17:18:55 -0700243 }
244 },
245 "xyz.openbmc_project.ObjectMapper",
246 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700247 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist787c3c32019-11-07 14:42:58 -0800248 interfaces);
James Feistb1728ca2020-04-30 15:40:55 -0700249
250 if constexpr (DEBUG)
251 {
252 std::cerr << __func__ << " " << __LINE__ << "\n";
253 }
James Feist3cb5fec2018-01-23 14:41:51 -0800254}
James Feistb1728ca2020-04-30 15:40:55 -0700255
James Feist8f2710a2018-05-09 17:18:55 -0700256// probes dbus interface dictionary for a key with a value that matches a regex
James Feist08a5b172019-08-28 14:47:47 -0700257bool probeDbus(const std::string& interface,
258 const std::map<std::string, nlohmann::json>& matches,
James Feist733f7652019-11-13 14:32:29 -0800259 FoundDeviceT& devices, std::shared_ptr<PerformScan> scan,
260 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800261{
James Feista465ccc2019-02-08 12:51:01 -0800262 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
James Feist733f7652019-11-13 14:32:29 -0800263 dbusObject = scan->dbusProbeObjects[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800264 if (dbusObject.empty())
265 {
James Feist8f2710a2018-05-09 17:18:55 -0700266 foundProbe = false;
267 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800268 }
269 foundProbe = true;
270
271 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800272 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800273 {
274 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800275 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800276 {
277 auto deviceValue = device.find(match.first);
278 if (deviceValue != device.end())
279 {
280 switch (match.second.type())
281 {
James Feist9eb0b582018-04-27 12:15:46 -0700282 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800283 {
James Feist9eb0b582018-04-27 12:15:46 -0700284 std::regex search(match.second.get<std::string>());
James Feist98132792019-07-09 13:29:09 -0700285 std::smatch regMatch;
James Feist9eb0b582018-04-27 12:15:46 -0700286
287 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800288 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700289 VariantToStringVisitor(), deviceValue->second);
James Feist98132792019-07-09 13:29:09 -0700290 if (!std::regex_search(probeValue, regMatch, search))
James Feist9eb0b582018-04-27 12:15:46 -0700291 {
292 deviceMatches = false;
293 break;
294 }
James Feist3cb5fec2018-01-23 14:41:51 -0800295 break;
296 }
James Feist9eb0b582018-04-27 12:15:46 -0700297 case nlohmann::json::value_t::boolean:
298 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800299 {
James Feista465ccc2019-02-08 12:51:01 -0800300 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700301 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800302
James Feist9eb0b582018-04-27 12:15:46 -0700303 if (probeValue != match.second.get<unsigned int>())
304 {
305 deviceMatches = false;
306 }
307 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800308 }
James Feist9eb0b582018-04-27 12:15:46 -0700309 case nlohmann::json::value_t::number_integer:
310 {
James Feista465ccc2019-02-08 12:51:01 -0800311 int probeValue = std::visit(VariantToIntVisitor(),
312 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800313
James Feist9eb0b582018-04-27 12:15:46 -0700314 if (probeValue != match.second.get<int>())
315 {
316 deviceMatches = false;
317 }
318 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800319 }
James Feist9eb0b582018-04-27 12:15:46 -0700320 case nlohmann::json::value_t::number_float:
321 {
James Feista465ccc2019-02-08 12:51:01 -0800322 float probeValue = std::visit(VariantToFloatVisitor(),
323 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700324
325 if (probeValue != match.second.get<float>())
326 {
327 deviceMatches = false;
328 }
329 break;
330 }
James Feist0eb40352019-04-09 14:44:04 -0700331 default:
332 {
333 std::cerr << "unexpected dbus probe type "
334 << match.second.type_name() << "\n";
Brad Bishop42124852020-08-19 17:17:01 -0400335 deviceMatches = false;
James Feist0eb40352019-04-09 14:44:04 -0700336 }
James Feist3cb5fec2018-01-23 14:41:51 -0800337 }
338 }
339 else
340 {
341 deviceMatches = false;
342 break;
343 }
344 }
345 if (deviceMatches)
346 {
James Feistf5125b02019-06-06 11:27:43 -0700347 devices.emplace_back(device);
James Feist3cb5fec2018-01-23 14:41:51 -0800348 foundMatch = true;
349 deviceMatches = false; // for next iteration
350 }
351 }
352 return foundMatch;
353}
354
355// default probe entry point, iterates a list looking for specific types to
356// call specific probe functions
357bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800358 const std::vector<std::string>& probeCommand,
James Feist733f7652019-11-13 14:32:29 -0800359 std::shared_ptr<PerformScan> scan,
James Feist08a5b172019-08-28 14:47:47 -0700360 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
361 foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800362{
363 const static std::regex command(R"(\((.*)\))");
364 std::smatch match;
365 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700366 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800367 bool cur = true;
368 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700369 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800370
James Feista465ccc2019-02-08 12:51:01 -0800371 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800372 {
373 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800374 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800375 cmp_str>::const_iterator probeType;
376
377 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
Patrick Venture4b7a7452019-10-28 08:41:02 -0700378 ++probeType)
James Feist3cb5fec2018-01-23 14:41:51 -0800379 {
380 if (probe.find(probeType->first) != std::string::npos)
381 {
382 foundProbe = true;
383 break;
384 }
385 }
386 if (foundProbe)
387 {
388 switch (probeType->second)
389 {
James Feist9eb0b582018-04-27 12:15:46 -0700390 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800391 {
James Feiste31e00a2019-07-24 10:45:43 -0700392 cur = false;
393 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800394 }
James Feist9eb0b582018-04-27 12:15:46 -0700395 case probe_type_codes::TRUE_T:
396 {
James Feiste31e00a2019-07-24 10:45:43 -0700397 cur = true;
398 break;
James Feist9eb0b582018-04-27 12:15:46 -0700399 }
400 case probe_type_codes::MATCH_ONE:
401 {
402 // set current value to last, this probe type shouldn't
403 // affect the outcome
404 cur = ret;
405 matchOne = true;
406 break;
407 }
408 /*case probe_type_codes::AND:
409 break;
410 case probe_type_codes::OR:
411 break;
412 // these are no-ops until the last command switch
413 */
414 case probe_type_codes::FOUND:
415 {
416 if (!std::regex_search(probe, match, command))
417 {
James Feist0eb40352019-04-09 14:44:04 -0700418 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700419 << "\n";
420 return false;
421 }
422 std::string commandStr = *(match.begin() + 1);
423 boost::replace_all(commandStr, "'", "");
James Feist733f7652019-11-13 14:32:29 -0800424 cur = (std::find(scan->passedProbes.begin(),
425 scan->passedProbes.end(),
426 commandStr) != scan->passedProbes.end());
James Feist9eb0b582018-04-27 12:15:46 -0700427 break;
428 }
James Feist0eb40352019-04-09 14:44:04 -0700429 default:
430 {
431 break;
432 }
James Feist3cb5fec2018-01-23 14:41:51 -0800433 }
434 }
435 // look on dbus for object
436 else
437 {
438 if (!std::regex_search(probe, match, command))
439 {
James Feist0eb40352019-04-09 14:44:04 -0700440 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800441 return false;
442 }
443 std::string commandStr = *(match.begin() + 1);
444 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700445 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800446 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800447 auto json = nlohmann::json::parse(commandStr, nullptr, false);
448 if (json.is_discarded())
449 {
James Feist0eb40352019-04-09 14:44:04 -0700450 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800451 return false;
452 }
453 // we can match any (string, variant) property. (string, string)
454 // does a regex
455 std::map<std::string, nlohmann::json> dbusProbeMap =
456 json.get<std::map<std::string, nlohmann::json>>();
457 auto findStart = probe.find("(");
458 if (findStart == std::string::npos)
459 {
460 return false;
461 }
462 std::string probeInterface = probe.substr(0, findStart);
James Feist733f7652019-11-13 14:32:29 -0800463 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
464 foundProbe);
James Feist3cb5fec2018-01-23 14:41:51 -0800465 }
466
467 // some functions like AND and OR only take affect after the
468 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700469 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800470 {
James Feist54a0dca2019-06-26 10:34:54 -0700471 ret = cur && ret;
472 }
473 else if (lastCommand == probe_type_codes::OR)
474 {
475 ret = cur || ret;
476 }
477
478 if (first)
479 {
480 ret = cur;
481 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800482 }
483 lastCommand = probeType != PROBE_TYPES.end()
484 ? probeType->second
485 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800486 }
487
488 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800489 if (ret && foundDevs.size() == 0)
490 {
James Feist08a5b172019-08-28 14:47:47 -0700491 foundDevs.emplace_back(
492 boost::container::flat_map<std::string, BasicVariantType>{});
James Feist3cb5fec2018-01-23 14:41:51 -0800493 }
James Feist0eb40352019-04-09 14:44:04 -0700494 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700495 {
James Feist71f295f2019-06-20 13:35:12 -0700496 // match the last one
497 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700498 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700499
500 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700501 }
James Feist3cb5fec2018-01-23 14:41:51 -0800502 return ret;
503}
504
James Feist7d807752019-11-13 14:47:57 -0800505PerformProbe::PerformProbe(const std::vector<std::string>& probeCommand,
506 std::shared_ptr<PerformScan>& scanPtr,
507 std::function<void(FoundDeviceT&)>&& callback) :
508 _probeCommand(probeCommand),
509 scan(scanPtr), _callback(std::move(callback))
James Feist8c505da2020-05-28 10:06:33 -0700510{}
James Feist7d807752019-11-13 14:47:57 -0800511PerformProbe::~PerformProbe()
512{
513 FoundDeviceT foundDevs;
514 if (probe(_probeCommand, scan, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700515 {
James Feist7d807752019-11-13 14:47:57 -0800516 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700517 }
James Feist7d807752019-11-13 14:47:57 -0800518}
James Feist8f2710a2018-05-09 17:18:55 -0700519
520// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800521bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800522{
James Feist1df06a42019-04-11 14:23:04 -0700523 std::filesystem::create_directory(configurationOutDir);
524 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700525 if (!output.good())
526 {
527 return false;
528 }
James Feist1b2e2242018-01-30 13:45:19 -0800529 output << systemConfiguration.dump(4);
530 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700531 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700532}
James Feist1b2e2242018-01-30 13:45:19 -0800533
James Feist97a63f12018-05-17 13:50:57 -0700534template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800535bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
536 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700537{
538 try
539 {
540 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800541 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700542 ref = value;
543 return true;
544 }
James Feist98132792019-07-09 13:29:09 -0700545 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700546 {
547 return false;
548 }
549}
James Feistbb43d022018-06-12 15:44:33 -0700550
James Feistebcc26b2019-03-22 12:30:43 -0700551// template function to add array as dbus property
552template <typename PropertyType>
553void addArrayToDbus(const std::string& name, const nlohmann::json& array,
554 sdbusplus::asio::dbus_interface* iface,
555 sdbusplus::asio::PropertyPermission permission,
556 nlohmann::json& systemConfiguration,
557 const std::string& jsonPointerString)
558{
559 std::vector<PropertyType> values;
560 for (const auto& property : array)
561 {
562 auto ptr = property.get_ptr<const PropertyType*>();
563 if (ptr != nullptr)
564 {
565 values.emplace_back(*ptr);
566 }
567 }
568
569 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
570 {
571 iface->register_property(name, values);
572 }
573 else
574 {
575 iface->register_property(
576 name, values,
577 [&systemConfiguration,
578 jsonPointerString{std::string(jsonPointerString)}](
579 const std::vector<PropertyType>& newVal,
580 std::vector<PropertyType>& val) {
581 val = newVal;
582 if (!setJsonFromPointer(jsonPointerString, val,
583 systemConfiguration))
584 {
585 std::cerr << "error setting json field\n";
586 return -1;
587 }
588 if (!writeJsonFiles(systemConfiguration))
589 {
590 std::cerr << "error setting json file\n";
591 return -1;
592 }
593 return 1;
594 });
595 }
596}
597
James Feistbb43d022018-06-12 15:44:33 -0700598template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800599void addProperty(const std::string& propertyName, const PropertyType& value,
600 sdbusplus::asio::dbus_interface* iface,
601 nlohmann::json& systemConfiguration,
602 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700603 sdbusplus::asio::PropertyPermission permission)
604{
605 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
606 {
607 iface->register_property(propertyName, value);
608 return;
609 }
James Feist68500ff2018-08-08 15:40:42 -0700610 iface->register_property(
611 propertyName, value,
612 [&systemConfiguration,
613 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800614 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700615 val = newVal;
616 if (!setJsonFromPointer(jsonPointerString, val,
617 systemConfiguration))
618 {
619 std::cerr << "error setting json field\n";
620 return -1;
621 }
James Feistc6248a52018-08-14 10:09:45 -0700622 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700623 {
624 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700625 return -1;
626 }
627 return 1;
628 });
629}
630
631void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800632 const std::string& jsonPointerPath,
633 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
634 sdbusplus::asio::object_server& objServer,
635 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700636{
637 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
638 iface->register_method(
639 "Delete", [&objServer, &systemConfiguration, interface,
640 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700641 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700642 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700643 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700644 {
645 // this technically can't happen as the pointer is pointing to
646 // us
647 throw DBusInternalError();
648 }
649 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feistc6248a52018-08-14 10:09:45 -0700650 systemConfiguration[ptr] = nullptr;
651
James Feist02d2b932020-02-06 16:28:48 -0800652 // todo(james): dig through sdbusplus to find out why we can't
653 // delete it in a method call
654 io.post([&objServer, dbusInterface]() mutable {
655 objServer.remove_interface(dbusInterface);
656 });
657
James Feistc6248a52018-08-14 10:09:45 -0700658 if (!writeJsonFiles(systemConfiguration))
659 {
660 std::cerr << "error setting json file\n";
661 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700662 }
James Feist68500ff2018-08-08 15:40:42 -0700663 });
James Feistbb43d022018-06-12 15:44:33 -0700664}
665
James Feist1b2e2242018-01-30 13:45:19 -0800666// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700667void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800668 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
669 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
670 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700671 sdbusplus::asio::PropertyPermission permission =
672 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800673{
James Feista465ccc2019-02-08 12:51:01 -0800674 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800675 {
James Feist8f2710a2018-05-09 17:18:55 -0700676 auto type = dictPair.value().type();
677 bool array = false;
678 if (dictPair.value().type() == nlohmann::json::value_t::array)
679 {
680 array = true;
681 if (!dictPair.value().size())
682 {
683 continue;
684 }
685 type = dictPair.value()[0].type();
686 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800687 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700688 {
689 if (arrayItem.type() != type)
690 {
691 isLegal = false;
692 break;
693 }
694 }
695 if (!isLegal)
696 {
697 std::cerr << "dbus format error" << dictPair.value() << "\n";
698 continue;
699 }
James Feista218ddb2019-04-11 14:01:31 -0700700 }
701 if (type == nlohmann::json::value_t::object)
702 {
703 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700704 }
James Feist97a63f12018-05-17 13:50:57 -0700705 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700706 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
707 {
708 // all setable numbers are doubles as it is difficult to always
709 // create a configuration file with all whole numbers as decimals
710 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700711 if (array)
712 {
713 if (dictPair.value()[0].is_number())
714 {
715 type = nlohmann::json::value_t::number_float;
716 }
717 }
718 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700719 {
720 type = nlohmann::json::value_t::number_float;
721 }
722 }
723
James Feist8f2710a2018-05-09 17:18:55 -0700724 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800725 {
James Feist9eb0b582018-04-27 12:15:46 -0700726 case (nlohmann::json::value_t::boolean):
727 {
James Feist8f2710a2018-05-09 17:18:55 -0700728 if (array)
729 {
730 // todo: array of bool isn't detected correctly by
731 // sdbusplus, change it to numbers
732 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700733 iface.get(), permission,
734 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700735 }
James Feistbb43d022018-06-12 15:44:33 -0700736
James Feist97a63f12018-05-17 13:50:57 -0700737 else
738 {
James Feistbb43d022018-06-12 15:44:33 -0700739 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700740 iface.get(), systemConfiguration, key,
741 permission);
James Feist97a63f12018-05-17 13:50:57 -0700742 }
James Feist9eb0b582018-04-27 12:15:46 -0700743 break;
744 }
745 case (nlohmann::json::value_t::number_integer):
746 {
James Feist8f2710a2018-05-09 17:18:55 -0700747 if (array)
748 {
749 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700750 iface.get(), permission,
751 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700752 }
753 else
754 {
James Feistbb43d022018-06-12 15:44:33 -0700755 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700756 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700757 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700758 }
James Feist9eb0b582018-04-27 12:15:46 -0700759 break;
760 }
761 case (nlohmann::json::value_t::number_unsigned):
762 {
James Feist8f2710a2018-05-09 17:18:55 -0700763 if (array)
764 {
765 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700766 iface.get(), permission,
767 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700768 }
769 else
770 {
James Feistbb43d022018-06-12 15:44:33 -0700771 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700772 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700773 systemConfiguration, key,
774 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700775 }
James Feist9eb0b582018-04-27 12:15:46 -0700776 break;
777 }
778 case (nlohmann::json::value_t::number_float):
779 {
James Feist8f2710a2018-05-09 17:18:55 -0700780 if (array)
781 {
782 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700783 iface.get(), permission,
784 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700785 }
James Feistbb43d022018-06-12 15:44:33 -0700786
James Feist97a63f12018-05-17 13:50:57 -0700787 else
788 {
James Feistbb43d022018-06-12 15:44:33 -0700789 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700790 iface.get(), systemConfiguration, key,
791 permission);
James Feist97a63f12018-05-17 13:50:57 -0700792 }
James Feist9eb0b582018-04-27 12:15:46 -0700793 break;
794 }
795 case (nlohmann::json::value_t::string):
796 {
James Feist8f2710a2018-05-09 17:18:55 -0700797 if (array)
798 {
James Feistebcc26b2019-03-22 12:30:43 -0700799 addArrayToDbus<std::string>(
800 dictPair.key(), dictPair.value(), iface.get(),
801 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700802 }
803 else
804 {
James Feistc6248a52018-08-14 10:09:45 -0700805 addProperty(
806 dictPair.key(), dictPair.value().get<std::string>(),
807 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700808 }
James Feist9eb0b582018-04-27 12:15:46 -0700809 break;
810 }
James Feist0eb40352019-04-09 14:44:04 -0700811 default:
812 {
James Feista218ddb2019-04-11 14:01:31 -0700813 std::cerr << "Unexpected json type in system configuration "
814 << dictPair.key() << ": "
815 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700816 break;
817 }
James Feist1b2e2242018-01-30 13:45:19 -0800818 }
819 }
James Feistc6248a52018-08-14 10:09:45 -0700820 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
821 {
822 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
823 systemConfiguration);
824 }
James Feist8f2710a2018-05-09 17:18:55 -0700825 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800826}
827
James Feista465ccc2019-02-08 12:51:01 -0800828sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700829{
830 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
831 interface) != settableInterfaces.end()
832 ? sdbusplus::asio::PropertyPermission::readWrite
833 : sdbusplus::asio::PropertyPermission::readOnly;
834}
835
James Feista465ccc2019-02-08 12:51:01 -0800836void createAddObjectMethod(const std::string& jsonPointerPath,
837 const std::string& path,
838 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700839 sdbusplus::asio::object_server& objServer,
840 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700841{
James Feistd58879a2019-09-11 11:26:07 -0700842 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
843 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700844
845 iface->register_method(
846 "AddObject",
847 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700848 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
849 board](const boost::container::flat_map<std::string, JsonVariantType>&
850 data) {
James Feist68500ff2018-08-08 15:40:42 -0700851 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800852 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700853 auto findExposes = base.find("Exposes");
854
855 if (findExposes == base.end())
856 {
857 throw std::invalid_argument("Entity must have children.");
858 }
859
860 // this will throw invalid-argument to sdbusplus if invalid json
861 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800862 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700863 {
James Feista465ccc2019-02-08 12:51:01 -0800864 nlohmann::json& newJson = newData[item.first];
865 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
866 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700867 }
868
869 auto findName = newData.find("Name");
870 auto findType = newData.find("Type");
871 if (findName == newData.end() || findType == newData.end())
872 {
873 throw std::invalid_argument("AddObject missing Name or Type");
874 }
James Feista465ccc2019-02-08 12:51:01 -0800875 const std::string* type = findType->get_ptr<const std::string*>();
876 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700877 if (type == nullptr || name == nullptr)
878 {
879 throw std::invalid_argument("Type and Name must be a string.");
880 }
881
James Feist02d2b932020-02-06 16:28:48 -0800882 bool foundNull = false;
James Feist68500ff2018-08-08 15:40:42 -0700883 size_t lastIndex = 0;
884 // we add in the "exposes"
James Feist02d2b932020-02-06 16:28:48 -0800885 for (const auto& expose : *findExposes)
James Feist68500ff2018-08-08 15:40:42 -0700886 {
James Feist02d2b932020-02-06 16:28:48 -0800887 if (expose.is_null())
888 {
889 foundNull = true;
890 continue;
891 }
892
893 if (expose["Name"] == *name && expose["Type"] == *type)
James Feist68500ff2018-08-08 15:40:42 -0700894 {
895 throw std::invalid_argument(
896 "Field already in JSON, not adding");
897 }
James Feist02d2b932020-02-06 16:28:48 -0800898
899 if (foundNull)
900 {
901 continue;
902 }
903
James Feist68500ff2018-08-08 15:40:42 -0700904 lastIndex++;
905 }
906
907 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
908 *type + ".json");
909 // todo(james) we might want to also make a list of 'can add'
910 // interfaces but for now I think the assumption if there is a
911 // schema avaliable that it is allowed to update is fine
912 if (!schemaFile.good())
913 {
914 throw std::invalid_argument(
915 "No schema avaliable, cannot validate.");
916 }
917 nlohmann::json schema =
918 nlohmann::json::parse(schemaFile, nullptr, false);
919 if (schema.is_discarded())
920 {
921 std::cerr << "Schema not legal" << *type << ".json\n";
922 throw DBusInternalError();
923 }
924 if (!validateJson(schema, newData))
925 {
926 throw std::invalid_argument("Data does not match schema");
927 }
James Feist02d2b932020-02-06 16:28:48 -0800928 if (foundNull)
929 {
930 findExposes->at(lastIndex) = newData;
931 }
932 else
933 {
934 findExposes->push_back(newData);
935 }
James Feist68500ff2018-08-08 15:40:42 -0700936 if (!writeJsonFiles(systemConfiguration))
937 {
938 std::cerr << "Error writing json files\n";
939 throw DBusInternalError();
940 }
941 std::string dbusName = *name;
942
943 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700944 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistd58879a2019-09-11 11:26:07 -0700945
946 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
947 createInterface(objServer, path + "/" + dbusName,
948 "xyz.openbmc_project.Configuration." + *type,
James Feist02d2b932020-02-06 16:28:48 -0800949 board, true);
James Feist68500ff2018-08-08 15:40:42 -0700950 // permission is read-write, as since we just created it, must be
951 // runtime modifiable
952 populateInterfaceFromJson(
953 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700954 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700955 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700956 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700957 });
958 iface->initialize();
959}
960
James Feista465ccc2019-02-08 12:51:01 -0800961void postToDbus(const nlohmann::json& newConfiguration,
962 nlohmann::json& systemConfiguration,
963 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800964
James Feist1b2e2242018-01-30 13:45:19 -0800965{
James Feist97a63f12018-05-17 13:50:57 -0700966 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800967 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800968 {
James Feistf1b14142019-04-10 15:22:09 -0700969 std::string boardKey = boardPair.value()["Name"];
James Feistd58879a2019-09-11 11:26:07 -0700970 std::string boardKeyOrig = boardPair.value()["Name"];
James Feistf1b14142019-04-10 15:22:09 -0700971 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700972 // loop through newConfiguration, but use values from system
973 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700974 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700975 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800976 std::string boardType;
977 if (findBoardType != boardValues.end() &&
978 findBoardType->type() == nlohmann::json::value_t::string)
979 {
980 boardType = findBoardType->get<std::string>();
981 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700982 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800983 }
984 else
985 {
986 std::cerr << "Unable to find type for " << boardKey
987 << " reverting to Chassis.\n";
988 boardType = "Chassis";
989 }
James Feist11be6672018-04-06 14:05:32 -0700990 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800991
992 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700993 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700994 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
995 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800996
James Feistd58879a2019-09-11 11:26:07 -0700997 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
998 createInterface(objServer, boardName,
999 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -07001000
James Feistd58879a2019-09-11 11:26:07 -07001001 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
1002 createInterface(objServer, boardName,
1003 "xyz.openbmc_project.Inventory.Item." + boardType,
1004 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -07001005
James Feist68500ff2018-08-08 15:40:42 -07001006 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -07001007 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -07001008
James Feist97a63f12018-05-17 13:50:57 -07001009 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001010 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -07001011 jsonPointerPath += "/";
1012 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -08001013 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -07001014 {
1015 if (boardField.value().type() == nlohmann::json::value_t::object)
1016 {
James Feistd58879a2019-09-11 11:26:07 -07001017 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
1018 createInterface(objServer, boardName, boardField.key(),
1019 boardKeyOrig);
1020
James Feistc6248a52018-08-14 10:09:45 -07001021 populateInterfaceFromJson(systemConfiguration,
1022 jsonPointerPath + boardField.key(),
1023 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -07001024 }
1025 }
James Feist97a63f12018-05-17 13:50:57 -07001026
James Feist1e3e6982018-08-03 16:09:28 -07001027 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -08001028 if (exposes == boardValues.end())
1029 {
1030 continue;
1031 }
James Feist97a63f12018-05-17 13:50:57 -07001032 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001033 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001034
1035 // store the board level pointer so we can modify it on the way down
1036 std::string jsonPointerPathBoard = jsonPointerPath;
1037 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001038 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001039 {
James Feist97a63f12018-05-17 13:50:57 -07001040 exposesIndex++;
1041 jsonPointerPath = jsonPointerPathBoard;
1042 jsonPointerPath += std::to_string(exposesIndex);
1043
James Feistd63d18a2018-07-19 15:23:45 -07001044 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001045 if (findName == item.end())
1046 {
1047 std::cerr << "cannot find name in field " << item << "\n";
1048 continue;
1049 }
James Feist1e3e6982018-08-03 16:09:28 -07001050 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001051 // if status is not found it is assumed to be status = 'okay'
1052 if (findStatus != item.end())
1053 {
1054 if (*findStatus == "disabled")
1055 {
1056 continue;
1057 }
1058 }
James Feistd63d18a2018-07-19 15:23:45 -07001059 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001060 std::string itemType;
1061 if (findType != item.end())
1062 {
1063 itemType = findType->get<std::string>();
1064 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001065 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1066 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001067 }
1068 else
1069 {
1070 itemType = "unknown";
1071 }
1072 std::string itemName = findName->get<std::string>();
1073 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001074 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001075
James Feistd58879a2019-09-11 11:26:07 -07001076 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
1077 createInterface(objServer, boardName + "/" + itemName,
1078 "xyz.openbmc_project.Configuration." + itemType,
1079 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -08001080
James Feist97a63f12018-05-17 13:50:57 -07001081 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001082 itemIface, item, objServer,
1083 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001084
James Feista465ccc2019-02-08 12:51:01 -08001085 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001086 {
James Feist97a63f12018-05-17 13:50:57 -07001087 jsonPointerPath = jsonPointerPathBoard +
1088 std::to_string(exposesIndex) + "/" +
1089 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001090 if (objectPair.value().type() ==
1091 nlohmann::json::value_t::object)
1092 {
James Feistd58879a2019-09-11 11:26:07 -07001093 std::shared_ptr<sdbusplus::asio::dbus_interface>
1094 objectIface = createInterface(
1095 objServer, boardName + "/" + itemName,
1096 "xyz.openbmc_project.Configuration." + itemType +
1097 "." + objectPair.key(),
1098 boardKeyOrig);
James Feist97a63f12018-05-17 13:50:57 -07001099
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +02001100 populateInterfaceFromJson(systemConfiguration,
1101 jsonPointerPath, objectIface,
1102 objectPair.value(), objServer,
1103 getPermission(objectPair.key()));
James Feist1b2e2242018-01-30 13:45:19 -08001104 }
1105 else if (objectPair.value().type() ==
1106 nlohmann::json::value_t::array)
1107 {
1108 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001109 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001110 {
James Feist8f2710a2018-05-09 17:18:55 -07001111 continue;
1112 }
1113 bool isLegal = true;
1114 auto type = objectPair.value()[0].type();
1115 if (type != nlohmann::json::value_t::object)
1116 {
1117 continue;
1118 }
1119
1120 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001121 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001122 {
1123 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001124 {
James Feist8f2710a2018-05-09 17:18:55 -07001125 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001126 break;
1127 }
James Feist8f2710a2018-05-09 17:18:55 -07001128 }
1129 if (!isLegal)
1130 {
1131 std::cerr << "dbus format error" << objectPair.value()
1132 << "\n";
1133 break;
1134 }
1135
James Feista465ccc2019-02-08 12:51:01 -08001136 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001137 {
James Feist97a63f12018-05-17 13:50:57 -07001138
James Feistd58879a2019-09-11 11:26:07 -07001139 std::shared_ptr<sdbusplus::asio::dbus_interface>
1140 objectIface = createInterface(
1141 objServer, boardName + "/" + itemName,
1142 "xyz.openbmc_project.Configuration." +
1143 itemType + "." + objectPair.key() +
1144 std::to_string(index),
1145 boardKeyOrig);
1146
James Feistc6248a52018-08-14 10:09:45 -07001147 populateInterfaceFromJson(
1148 systemConfiguration,
1149 jsonPointerPath + "/" + std::to_string(index),
1150 objectIface, arrayItem, objServer,
1151 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001152 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001153 }
1154 }
1155 }
1156 }
1157 }
1158}
1159
James Feist8f2710a2018-05-09 17:18:55 -07001160// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001161bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001162{
1163 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001164 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001165 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1166 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001167 {
1168 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001169 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001170 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001171 }
James Feistb4383f42018-08-06 16:54:10 -07001172
1173 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1174 globalSchema);
1175 if (!schemaStream.good())
1176 {
1177 std::cerr
1178 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1179 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001180 return false;
James Feistb4383f42018-08-06 16:54:10 -07001181 }
1182 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1183 if (schema.is_discarded())
1184 {
1185 std::cerr
1186 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1187 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001188 return false;
James Feistb4383f42018-08-06 16:54:10 -07001189 }
1190
James Feista465ccc2019-02-08 12:51:01 -08001191 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001192 {
1193 std::ifstream jsonStream(jsonPath.c_str());
1194 if (!jsonStream.good())
1195 {
1196 std::cerr << "unable to open " << jsonPath.string() << "\n";
1197 continue;
1198 }
1199 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1200 if (data.is_discarded())
1201 {
1202 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1203 continue;
1204 }
James Feist8da99192019-01-24 08:20:16 -08001205 /*
1206 * todo(james): reenable this once less things are in flight
1207 *
James Feistb4383f42018-08-06 16:54:10 -07001208 if (!validateJson(schema, data))
1209 {
1210 std::cerr << "Error validating " << jsonPath.string() << "\n";
1211 continue;
1212 }
James Feist8da99192019-01-24 08:20:16 -08001213 */
James Feistb4383f42018-08-06 16:54:10 -07001214
James Feist3cb5fec2018-01-23 14:41:51 -08001215 if (data.type() == nlohmann::json::value_t::array)
1216 {
James Feista465ccc2019-02-08 12:51:01 -08001217 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001218 {
1219 configurations.emplace_back(d);
1220 }
1221 }
1222 else
1223 {
1224 configurations.emplace_back(data);
1225 }
1226 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001227 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001228}
James Feist3cb5fec2018-01-23 14:41:51 -08001229
James Feist94219d12020-03-03 11:52:25 -08001230std::string getRecordName(
1231 const boost::container::flat_map<std::string, BasicVariantType>& probe,
1232 const std::string& probeName)
1233{
1234 if (probe.empty())
1235 {
1236 return probeName;
1237 }
1238
1239 // use an array so alphabetical order from the
1240 // flat_map is maintained
1241 auto device = nlohmann::json::array();
1242 for (auto& devPair : probe)
1243 {
1244 device.push_back(devPair.first);
1245 std::visit([&device](auto&& v) { device.push_back(v); },
1246 devPair.second);
1247 }
1248 size_t hash = std::hash<std::string>{}(probeName + device.dump());
1249 // hashes are hard to distinguish, use the
1250 // non-hashed version if we want debug
1251 if constexpr (DEBUG)
1252 {
1253 return probeName + device.dump();
1254 }
1255 else
1256 {
1257 return std::to_string(hash);
1258 }
1259}
1260
James Feist4dc617b2020-05-01 09:54:47 -07001261PerformScan::PerformScan(nlohmann::json& systemConfiguration,
1262 nlohmann::json& missingConfigurations,
1263 std::list<nlohmann::json>& configurations,
1264 sdbusplus::asio::object_server& objServerIn,
1265 std::function<void()>&& callback) :
James Feist733f7652019-11-13 14:32:29 -08001266 _systemConfiguration(systemConfiguration),
1267 _missingConfigurations(missingConfigurations),
James Feist4dc617b2020-05-01 09:54:47 -07001268 _configurations(configurations), objServer(objServerIn),
1269 _callback(std::move(callback))
James Feist8c505da2020-05-28 10:06:33 -07001270{}
James Feist733f7652019-11-13 14:32:29 -08001271void PerformScan::run()
1272{
1273 boost::container::flat_set<std::string> dbusProbeInterfaces;
1274 std::vector<std::shared_ptr<PerformProbe>> dbusProbePointers;
James Feist75fdeeb2018-02-20 14:26:16 -08001275
James Feist733f7652019-11-13 14:32:29 -08001276 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001277 {
James Feist733f7652019-11-13 14:32:29 -08001278 auto findProbe = it->find("Probe");
1279 auto findName = it->find("Name");
James Feist787c3c32019-11-07 14:42:58 -08001280
James Feist733f7652019-11-13 14:32:29 -08001281 nlohmann::json probeCommand;
1282 // check for poorly formatted fields, probe must be an array
1283 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001284 {
James Feist733f7652019-11-13 14:32:29 -08001285 std::cerr << "configuration file missing probe:\n " << *it << "\n";
1286 it = _configurations.erase(it);
1287 continue;
1288 }
1289 else if ((*findProbe).type() != nlohmann::json::value_t::array)
1290 {
1291 probeCommand = nlohmann::json::array();
1292 probeCommand.push_back(*findProbe);
1293 }
1294 else
1295 {
1296 probeCommand = *findProbe;
1297 }
James Feist3cb5fec2018-01-23 14:41:51 -08001298
James Feist733f7652019-11-13 14:32:29 -08001299 if (findName == it->end())
1300 {
1301 std::cerr << "configuration file missing name:\n " << *it << "\n";
1302 it = _configurations.erase(it);
1303 continue;
1304 }
1305 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001306
James Feist733f7652019-11-13 14:32:29 -08001307 if (std::find(passedProbes.begin(), passedProbes.end(), probeName) !=
1308 passedProbes.end())
1309 {
1310 it = _configurations.erase(it);
1311 continue;
1312 }
1313 nlohmann::json* recordPtr = &(*it);
James Feist1b2e2242018-01-30 13:45:19 -08001314
James Feist733f7652019-11-13 14:32:29 -08001315 // store reference to this to children to makes sure we don't get
1316 // destroyed too early
1317 auto thisRef = shared_from_this();
1318 auto probePointer = std::make_shared<PerformProbe>(
1319 probeCommand, thisRef,
1320 [&, recordPtr, probeName](FoundDeviceT& foundDevices) {
James Feist08a5b172019-08-28 14:47:47 -07001321 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001322
James Feist35f5e0e2020-03-16 14:02:27 -07001323 std::set<nlohmann::json> usedNames;
James Feist733f7652019-11-13 14:32:29 -08001324 passedProbes.push_back(probeName);
James Feist94219d12020-03-03 11:52:25 -08001325 std::list<size_t> indexes(foundDevices.size());
1326 std::iota(indexes.begin(), indexes.end(), 1);
James Feist8f2710a2018-05-09 17:18:55 -07001327
James Feist35f5e0e2020-03-16 14:02:27 -07001328 size_t indexIdx = probeName.find("$");
1329 bool hasTemplateName = (indexIdx != std::string::npos);
James Feist94219d12020-03-03 11:52:25 -08001330
1331 // copy over persisted configurations and make sure we remove
1332 // indexes that are already used
1333 for (auto itr = foundDevices.begin();
1334 itr != foundDevices.end();)
James Feist08a5b172019-08-28 14:47:47 -07001335 {
James Feist94219d12020-03-03 11:52:25 -08001336 std::string recordName = getRecordName(*itr, probeName);
James Feistf1b14142019-04-10 15:22:09 -07001337
James Feist08a5b172019-08-28 14:47:47 -07001338 auto fromLastJson = lastJson.find(recordName);
1339 if (fromLastJson != lastJson.end())
1340 {
James Feist02d2b932020-02-06 16:28:48 -08001341 auto findExposes = fromLastJson->find("Exposes");
1342 // delete nulls from any updates
1343 if (findExposes != fromLastJson->end())
1344 {
1345 auto copy = nlohmann::json::array();
1346 for (auto& expose : *findExposes)
1347 {
1348 if (expose.is_null())
1349 {
1350 continue;
1351 }
1352 copy.emplace_back(expose);
1353 }
1354 *findExposes = copy;
1355 }
1356
James Feist08a5b172019-08-28 14:47:47 -07001357 // keep user changes
1358 _systemConfiguration[recordName] = *fromLastJson;
James Feist899e17f2019-09-13 11:46:29 -07001359 _missingConfigurations.erase(recordName);
James Feist94219d12020-03-03 11:52:25 -08001360 itr = foundDevices.erase(itr);
James Feist35f5e0e2020-03-16 14:02:27 -07001361 if (hasTemplateName)
James Feist94219d12020-03-03 11:52:25 -08001362 {
1363 auto nameIt = fromLastJson->find("Name");
1364 if (nameIt == fromLastJson->end())
1365 {
1366 std::cerr << "Last JSON Illegal\n";
1367 continue;
1368 }
1369
1370 int index = std::stoi(
1371 nameIt->get<std::string>().substr(indexIdx),
1372 nullptr, 0);
James Feist35f5e0e2020-03-16 14:02:27 -07001373 usedNames.insert(nameIt.value());
James Feist94219d12020-03-03 11:52:25 -08001374 auto usedIt = std::find(indexes.begin(),
1375 indexes.end(), index);
1376
1377 if (usedIt == indexes.end())
1378 {
1379 continue; // less items now
1380 }
1381 indexes.erase(usedIt);
1382 }
1383
James Feist08a5b172019-08-28 14:47:47 -07001384 continue;
1385 }
James Feist94219d12020-03-03 11:52:25 -08001386 itr++;
1387 }
James Feist35f5e0e2020-03-16 14:02:27 -07001388
1389 std::optional<std::string> replaceStr;
1390
James Feist94219d12020-03-03 11:52:25 -08001391 for (auto& foundDevice : foundDevices)
1392 {
1393 nlohmann::json record = *recordPtr;
1394 std::string recordName =
1395 getRecordName(foundDevice, probeName);
1396 size_t foundDeviceIdx = indexes.front();
1397 indexes.pop_front();
James Feistf1b14142019-04-10 15:22:09 -07001398
James Feist35f5e0e2020-03-16 14:02:27 -07001399 // check name first so we have no duplicate names
1400 auto getName = record.find("Name");
1401 if (getName == record.end())
1402 {
1403 std::cerr << "Record Missing Name! " << record.dump();
1404 continue; // this should be impossible at this level
1405 }
1406
1407 nlohmann::json copyForName = {{"Name", getName.value()}};
1408 nlohmann::json::iterator copyIt = copyForName.begin();
1409 std::optional<std::string> replaceVal = templateCharReplace(
1410 copyIt, foundDevice, foundDeviceIdx, replaceStr);
1411
1412 if (!replaceStr && replaceVal)
1413 {
1414 if (usedNames.find(copyIt.value()) != usedNames.end())
1415 {
1416 replaceStr = replaceVal;
1417 copyForName = {{"Name", getName.value()}};
1418 copyIt = copyForName.begin();
1419 templateCharReplace(copyIt, foundDevice,
1420 foundDeviceIdx, replaceStr);
1421 }
1422 }
1423
1424 if (replaceStr)
1425 {
1426 std::cerr << "Duplicates found, replacing "
1427 << *replaceStr
1428 << " with found device index.\n Consider "
1429 "fixing template to not have duplicates\n";
1430 }
James Feist08a5b172019-08-28 14:47:47 -07001431
1432 for (auto keyPair = record.begin(); keyPair != record.end();
1433 keyPair++)
1434 {
James Feist35f5e0e2020-03-16 14:02:27 -07001435 if (keyPair.key() == "Name")
1436 {
1437 keyPair.value() = copyIt.value();
1438 usedNames.insert(copyIt.value());
1439
1440 continue; // already covered above
1441 }
James Feist08a5b172019-08-28 14:47:47 -07001442 templateCharReplace(keyPair, foundDevice,
James Feist35f5e0e2020-03-16 14:02:27 -07001443 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001444 }
1445
James Feist35f5e0e2020-03-16 14:02:27 -07001446 // insert into configuration temporarily to be able to
1447 // reference ourselves
1448
1449 _systemConfiguration[recordName] = record;
1450
James Feist08a5b172019-08-28 14:47:47 -07001451 auto findExpose = record.find("Exposes");
1452 if (findExpose == record.end())
1453 {
James Feistf1b14142019-04-10 15:22:09 -07001454 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001455 continue;
1456 }
James Feistf1b14142019-04-10 15:22:09 -07001457
James Feist08a5b172019-08-28 14:47:47 -07001458 for (auto& expose : *findExpose)
1459 {
1460 for (auto keyPair = expose.begin();
1461 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001462 {
James Feist08a5b172019-08-28 14:47:47 -07001463
1464 templateCharReplace(keyPair, foundDevice,
James Feist35f5e0e2020-03-16 14:02:27 -07001465 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001466
James Feist668bbb12020-02-05 14:27:26 -08001467 bool isBind =
1468 boost::starts_with(keyPair.key(), "Bind");
1469 bool isDisable = keyPair.key() == "DisableNode";
1470
1471 // special cases
1472 if (!(isBind || isDisable))
James Feistf1b14142019-04-10 15:22:09 -07001473 {
James Feist668bbb12020-02-05 14:27:26 -08001474 continue;
1475 }
1476
1477 if (keyPair.value().type() !=
1478 nlohmann::json::value_t::string &&
1479 keyPair.value().type() !=
1480 nlohmann::json::value_t::array)
1481 {
1482 std::cerr << "Value is invalid type "
1483 << keyPair.key() << "\n";
1484 continue;
1485 }
1486
1487 std::vector<std::string> matches;
1488 if (keyPair.value().type() ==
1489 nlohmann::json::value_t::string)
1490 {
1491 matches.emplace_back(keyPair.value());
1492 }
1493 else
1494 {
1495 for (const auto& value : keyPair.value())
James Feistf1b14142019-04-10 15:22:09 -07001496 {
James Feist668bbb12020-02-05 14:27:26 -08001497 if (value.type() !=
1498 nlohmann::json::value_t::string)
1499 {
1500 std::cerr << "Value is invalid type "
1501 << value << "\n";
1502 break;
1503 }
1504 matches.emplace_back(value);
James Feistf1b14142019-04-10 15:22:09 -07001505 }
James Feist668bbb12020-02-05 14:27:26 -08001506 }
James Feist08a5b172019-08-28 14:47:47 -07001507
James Feist668bbb12020-02-05 14:27:26 -08001508 std::set<std::string> foundMatches;
1509 for (auto& configurationPair :
1510 _systemConfiguration.items())
1511 {
1512 if (isDisable)
James Feist8f2710a2018-05-09 17:18:55 -07001513 {
James Feist668bbb12020-02-05 14:27:26 -08001514 // don't disable ourselves
1515 if (configurationPair.key() == recordName)
James Feist3cb5fec2018-01-23 14:41:51 -08001516 {
James Feist1b2e2242018-01-30 13:45:19 -08001517 continue;
1518 }
James Feist668bbb12020-02-05 14:27:26 -08001519 }
1520 auto configListFind =
1521 configurationPair.value().find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001522
James Feist668bbb12020-02-05 14:27:26 -08001523 if (configListFind ==
1524 configurationPair.value().end() ||
1525 configListFind->type() !=
1526 nlohmann::json::value_t::array)
James Feist08a5b172019-08-28 14:47:47 -07001527 {
James Feist668bbb12020-02-05 14:27:26 -08001528 continue;
James Feist08a5b172019-08-28 14:47:47 -07001529 }
James Feist668bbb12020-02-05 14:27:26 -08001530 for (auto& exposedObject : *configListFind)
1531 {
1532 auto matchIt = std::find_if(
1533 matches.begin(), matches.end(),
1534 [name = (exposedObject)["Name"]
1535 .get<std::string>()](
1536 const std::string& s) {
1537 return s == name;
1538 });
1539 if (matchIt == matches.end())
1540 {
1541 continue;
1542 }
1543 foundMatches.insert(*matchIt);
1544
1545 if (isBind)
1546 {
1547 std::string bind = keyPair.key().substr(
1548 sizeof("Bind") - 1);
1549
1550 exposedObject["Status"] = "okay";
1551 expose[bind] = exposedObject;
1552 }
1553 else if (isDisable)
1554 {
1555 exposedObject["Status"] = "disabled";
1556 }
1557 }
1558 }
1559 if (foundMatches.size() != matches.size())
1560 {
1561 std::cerr << "configuration file "
1562 "dependency error, "
1563 "could not find "
1564 << keyPair.key() << " "
1565 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001566 }
1567 }
1568 }
James Feist08a5b172019-08-28 14:47:47 -07001569 // overwrite ourselves with cleaned up version
1570 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001571 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001572 }
1573 });
James Feist787c3c32019-11-07 14:42:58 -08001574
James Feist733f7652019-11-13 14:32:29 -08001575 // parse out dbus probes by discarding other probe types, store in a
1576 // map
1577 for (const std::string& probe : probeCommand)
1578 {
1579 bool found = false;
1580 boost::container::flat_map<const char*, probe_type_codes,
1581 cmp_str>::const_iterator probeType;
1582 for (probeType = PROBE_TYPES.begin();
1583 probeType != PROBE_TYPES.end(); ++probeType)
James Feist787c3c32019-11-07 14:42:58 -08001584 {
James Feist733f7652019-11-13 14:32:29 -08001585 if (probe.find(probeType->first) != std::string::npos)
James Feist787c3c32019-11-07 14:42:58 -08001586 {
James Feist733f7652019-11-13 14:32:29 -08001587 found = true;
1588 break;
James Feist787c3c32019-11-07 14:42:58 -08001589 }
James Feist787c3c32019-11-07 14:42:58 -08001590 }
James Feist733f7652019-11-13 14:32:29 -08001591 if (found)
1592 {
1593 continue;
1594 }
1595 // syntax requires probe before first open brace
1596 auto findStart = probe.find("(");
1597 std::string interface = probe.substr(0, findStart);
1598 dbusProbeInterfaces.emplace(interface);
1599 dbusProbePointers.emplace_back(probePointer);
James Feist3cb5fec2018-01-23 14:41:51 -08001600 }
James Feist733f7652019-11-13 14:32:29 -08001601 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001602 }
James Feist75fdeeb2018-02-20 14:26:16 -08001603
James Feist4dc617b2020-05-01 09:54:47 -07001604 for (const std::string& interface : dbusProbeInterfaces)
1605 {
1606 registerCallback(_systemConfiguration, objServer, interface);
1607 }
1608
James Feist733f7652019-11-13 14:32:29 -08001609 // probe vector stores a shared_ptr to each PerformProbe that cares
1610 // about a dbus interface
James Feistb1728ca2020-04-30 15:40:55 -07001611 findDbusObjects(std::move(dbusProbePointers),
1612 std::move(dbusProbeInterfaces), shared_from_this());
1613 if constexpr (DEBUG)
1614 {
1615 std::cerr << __func__ << " " << __LINE__ << "\n";
1616 }
James Feist733f7652019-11-13 14:32:29 -08001617}
1618
1619PerformScan::~PerformScan()
1620{
1621 if (_passed)
James Feist75fdeeb2018-02-20 14:26:16 -08001622 {
James Feist733f7652019-11-13 14:32:29 -08001623 auto nextScan = std::make_shared<PerformScan>(
1624 _systemConfiguration, _missingConfigurations, _configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001625 objServer, std::move(_callback));
James Feist733f7652019-11-13 14:32:29 -08001626 nextScan->passedProbes = std::move(passedProbes);
1627 nextScan->dbusProbeObjects = std::move(dbusProbeObjects);
1628 nextScan->run();
James Feistb1728ca2020-04-30 15:40:55 -07001629
1630 if constexpr (DEBUG)
1631 {
1632 std::cerr << __func__ << " " << __LINE__ << "\n";
1633 }
James Feist8f2710a2018-05-09 17:18:55 -07001634 }
James Feist733f7652019-11-13 14:32:29 -08001635 else
1636 {
James Feist4dc617b2020-05-01 09:54:47 -07001637 _callback();
James Feistb1728ca2020-04-30 15:40:55 -07001638
1639 if constexpr (DEBUG)
1640 {
1641 std::cerr << __func__ << " " << __LINE__ << "\n";
1642 }
James Feist733f7652019-11-13 14:32:29 -08001643 }
1644}
James Feistc95cb142018-02-26 10:41:42 -08001645
James Feistb1728ca2020-04-30 15:40:55 -07001646void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001647 nlohmann::json& systemConfiguration)
1648{
1649 static bool scannedPowerOff = false;
1650 static bool scannedPowerOn = false;
1651
James Feistfb00f392019-06-25 14:16:48 -07001652 if (systemConfiguration.empty() || lastJson.empty())
1653 {
1654 return; // not ready yet
1655 }
James Feist1df06a42019-04-11 14:23:04 -07001656 if (scannedPowerOn)
1657 {
1658 return;
1659 }
1660
1661 if (!isPowerOn() && scannedPowerOff)
1662 {
1663 return;
1664 }
1665
James Feistb1728ca2020-04-30 15:40:55 -07001666 timer.expires_after(std::chrono::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001667 timer.async_wait(
1668 [&systemConfiguration](const boost::system::error_code& ec) {
1669 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001670 {
James Feist1a996582019-05-14 15:10:06 -07001671 // we were cancelled
1672 return;
1673 }
1674
1675 bool powerOff = !isPowerOn();
1676 for (const auto& item : lastJson.items())
1677 {
1678 if (systemConfiguration.find(item.key()) ==
1679 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001680 {
James Feist1a996582019-05-14 15:10:06 -07001681 bool isDetectedPowerOn = false;
1682 auto powerState = item.value().find("PowerState");
1683 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001684 {
James Feist1a996582019-05-14 15:10:06 -07001685 auto ptr = powerState->get_ptr<const std::string*>();
1686 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001687 {
James Feist1a996582019-05-14 15:10:06 -07001688 if (*ptr == "On" || *ptr == "BiosPost")
1689 {
1690 isDetectedPowerOn = true;
1691 }
James Feist1df06a42019-04-11 14:23:04 -07001692 }
1693 }
James Feist1a996582019-05-14 15:10:06 -07001694 if (powerOff && isDetectedPowerOn)
1695 {
1696 // power not on yet, don't know if it's there or not
1697 continue;
1698 }
1699 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1700 {
1701 // already logged it when power was off
1702 continue;
1703 }
James Feist1df06a42019-04-11 14:23:04 -07001704
James Feist1a996582019-05-14 15:10:06 -07001705 logDeviceRemoved(item.value());
1706 }
James Feist1df06a42019-04-11 14:23:04 -07001707 }
James Feist1a996582019-05-14 15:10:06 -07001708 scannedPowerOff = true;
1709 if (!powerOff)
1710 {
1711 scannedPowerOn = true;
1712 }
1713 });
James Feist1df06a42019-04-11 14:23:04 -07001714}
1715
James Feist8f2710a2018-05-09 17:18:55 -07001716// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -07001717void propertiesChangedCallback(nlohmann::json& systemConfiguration,
1718 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001719{
James Feist2539ccd2020-05-01 16:15:08 -07001720 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -07001721 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001722 static size_t instance = 0;
1723 instance++;
1724 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001725
James Feistb1728ca2020-04-30 15:40:55 -07001726 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001727
1728 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -07001729 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -07001730 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001731 if (ec == boost::asio::error::operation_aborted)
1732 {
1733 // we were cancelled
1734 return;
1735 }
1736 else if (ec)
1737 {
1738 std::cerr << "async wait error " << ec << "\n";
1739 return;
1740 }
1741
James Feist2539ccd2020-05-01 16:15:08 -07001742 if (inProgress)
1743 {
1744 propertiesChangedCallback(systemConfiguration, objServer);
1745 return;
1746 }
1747 inProgress = true;
1748
James Feist8f2710a2018-05-09 17:18:55 -07001749 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001750 auto missingConfigurations = std::make_shared<nlohmann::json>();
1751 *missingConfigurations = systemConfiguration;
1752
James Feist8f2710a2018-05-09 17:18:55 -07001753 std::list<nlohmann::json> configurations;
1754 if (!findJsonFiles(configurations))
1755 {
1756 std::cerr << "cannot find json files\n";
James Feist2539ccd2020-05-01 16:15:08 -07001757 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -07001758 return;
1759 }
1760
1761 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001762 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001763 objServer,
1764 [&systemConfiguration, &objServer, count, oldConfiguration,
1765 missingConfigurations]() {
James Feist899e17f2019-09-13 11:46:29 -07001766 // this is something that since ac has been applied to the bmc
1767 // we saw, and we no longer see it
1768 bool powerOff = !isPowerOn();
1769 for (const auto& item : missingConfigurations->items())
1770 {
1771 bool isDetectedPowerOn = false;
1772 auto powerState = item.value().find("PowerState");
1773 if (powerState != item.value().end())
1774 {
1775 auto ptr = powerState->get_ptr<const std::string*>();
1776 if (ptr)
1777 {
1778 if (*ptr == "On" || *ptr == "BiosPost")
1779 {
1780 isDetectedPowerOn = true;
1781 }
1782 }
1783 }
1784 if (powerOff && isDetectedPowerOn)
1785 {
1786 // power not on yet, don't know if it's there or not
1787 continue;
1788 }
1789 std::string name = item.value()["Name"].get<std::string>();
James Feist02d2b932020-02-06 16:28:48 -08001790 std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
James Feist899e17f2019-09-13 11:46:29 -07001791 ifaces = inventory[name];
1792 for (auto& iface : ifaces)
1793 {
James Feist02d2b932020-02-06 16:28:48 -08001794 auto sharedPtr = iface.lock();
1795 if (!sharedPtr)
1796 {
1797 continue; // was already deleted elsewhere
1798 }
1799 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -07001800 }
1801 ifaces.clear();
1802 systemConfiguration.erase(item.key());
1803 logDeviceRemoved(item.value());
1804 }
1805
James Feist8f2710a2018-05-09 17:18:55 -07001806 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001807 for (auto it = newConfiguration.begin();
1808 it != newConfiguration.end();)
1809 {
1810 auto findKey = oldConfiguration.find(it.key());
1811 if (findKey != oldConfiguration.end())
1812 {
1813 it = newConfiguration.erase(it);
1814 }
1815 else
1816 {
1817 it++;
1818 }
1819 }
James Feist899e17f2019-09-13 11:46:29 -07001820 for (const auto& item : newConfiguration.items())
1821 {
1822 logDeviceAdded(item.value());
1823 }
1824
James Feist2539ccd2020-05-01 16:15:08 -07001825 inProgress = false;
1826
James Feist8f2710a2018-05-09 17:18:55 -07001827 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001828 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001829
James Feistbb43d022018-06-12 15:44:33 -07001830 io.post([&]() {
1831 if (!writeJsonFiles(systemConfiguration))
1832 {
1833 std::cerr << "Error writing json files\n";
1834 }
1835 });
James Feist8f2710a2018-05-09 17:18:55 -07001836 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001837 postToDbus(newConfiguration, systemConfiguration,
1838 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001839 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001840 {
James Feist899e17f2019-09-13 11:46:29 -07001841 return;
James Feist1df06a42019-04-11 14:23:04 -07001842 }
James Feist899e17f2019-09-13 11:46:29 -07001843 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001844 });
1845 });
1846 });
1847 perfScan->run();
1848 });
James Feist75fdeeb2018-02-20 14:26:16 -08001849}
1850
James Feist4dc617b2020-05-01 09:54:47 -07001851void registerCallback(nlohmann::json& systemConfiguration,
1852 sdbusplus::asio::object_server& objServer,
1853 const std::string& interface)
James Feist75fdeeb2018-02-20 14:26:16 -08001854{
James Feist4dc617b2020-05-01 09:54:47 -07001855 static boost::container::flat_map<std::string, sdbusplus::bus::match::match>
1856 dbusMatches;
James Feist75fdeeb2018-02-20 14:26:16 -08001857
James Feist4dc617b2020-05-01 09:54:47 -07001858 auto find = dbusMatches.find(interface);
1859 if (find != dbusMatches.end())
James Feist75fdeeb2018-02-20 14:26:16 -08001860 {
James Feist4dc617b2020-05-01 09:54:47 -07001861 return;
James Feist75fdeeb2018-02-20 14:26:16 -08001862 }
James Feist4dc617b2020-05-01 09:54:47 -07001863 std::function<void(sdbusplus::message::message & message)> eventHandler =
1864
1865 [&](sdbusplus::message::message&) {
1866 propertiesChangedCallback(systemConfiguration, objServer);
1867 };
1868
1869 sdbusplus::bus::match::match match(
1870 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
1871 "type='signal',member='PropertiesChanged',arg0='" + interface + "'",
1872 eventHandler);
1873 dbusMatches.emplace(interface, std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001874}
1875
James Feist98132792019-07-09 13:29:09 -07001876int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001877{
1878 // setup connection to dbus
James Feist8f2710a2018-05-09 17:18:55 -07001879 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001880 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001881
James Feist8f2710a2018-05-09 17:18:55 -07001882 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001883
James Feist8f2710a2018-05-09 17:18:55 -07001884 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1885 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1886 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001887
James Feist4131aea2018-03-09 09:47:30 -08001888 // to keep reference to the match / filter objects so they don't get
1889 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001890
1891 nlohmann::json systemConfiguration = nlohmann::json::object();
1892
James Feist4dc617b2020-05-01 09:54:47 -07001893 io.post(
1894 [&]() { propertiesChangedCallback(systemConfiguration, objServer); });
James Feist4131aea2018-03-09 09:47:30 -08001895
James Feistfd1264a2018-05-03 12:10:00 -07001896 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001897 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001898 });
James Feist8f2710a2018-05-09 17:18:55 -07001899 entityIface->initialize();
1900
James Feist1df06a42019-04-11 14:23:04 -07001901 if (fwVersionIsSame())
1902 {
1903 if (std::filesystem::is_regular_file(currentConfiguration))
1904 {
1905 // this file could just be deleted, but it's nice for debug
1906 std::filesystem::create_directory(tempConfigDir);
1907 std::filesystem::remove(lastConfiguration);
1908 std::filesystem::copy(currentConfiguration, lastConfiguration);
1909 std::filesystem::remove(currentConfiguration);
1910
1911 std::ifstream jsonStream(lastConfiguration);
1912 if (jsonStream.good())
1913 {
1914 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1915 if (data.is_discarded())
1916 {
1917 std::cerr << "syntax error in " << lastConfiguration
1918 << "\n";
1919 }
1920 else
1921 {
1922 lastJson = std::move(data);
1923 }
1924 }
1925 else
1926 {
1927 std::cerr << "unable to open " << lastConfiguration << "\n";
1928 }
1929 }
1930 }
1931 else
1932 {
1933 // not an error, just logging at this level to make it in the journal
1934 std::cerr << "Clearing previous configuration\n";
1935 std::filesystem::remove(currentConfiguration);
1936 }
1937
1938 // some boards only show up after power is on, we want to not say they are
1939 // removed until the same state happens
1940 setupPowerMatch(SYSTEM_BUS);
1941
James Feist1b2e2242018-01-30 13:45:19 -08001942 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001943
1944 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001945}