blob: c73a69bc4d044bfa9afdcd28ef8ebebea05f4138 [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 Feista465ccc2019-02-08 12:51:01 -080017#include "filesystem.hpp"
18
James Feistc95cb142018-02-26 10:41:42 -080019#include <Overlay.hpp>
James Feista465ccc2019-02-08 12:51:01 -080020#include <Utils.hpp>
21#include <VariantVisitors.hpp>
James Feist11be6672018-04-06 14:05:32 -070022#include <boost/algorithm/string/case_conv.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080023#include <boost/algorithm/string/predicate.hpp>
24#include <boost/algorithm/string/replace.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080025#include <boost/container/flat_map.hpp>
26#include <boost/container/flat_set.hpp>
James Feista465ccc2019-02-08 12:51:01 -080027#include <boost/lexical_cast.hpp>
28#include <fstream>
29#include <iostream>
30#include <nlohmann/json.hpp>
31#include <regex>
32#include <sdbusplus/asio/connection.hpp>
33#include <sdbusplus/asio/object_server.hpp>
34#include <variant>
James Feist3cb5fec2018-01-23 14:41:51 -080035
James Feista465ccc2019-02-08 12:51:01 -080036constexpr const char* OUTPUT_DIR = "/var/configuration/";
37constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
38constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
39constexpr const char* globalSchema = "global.json";
40constexpr const char* TEMPLATE_CHAR = "$";
James Feist8f2710a2018-05-09 17:18:55 -070041constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080042
James Feistf1b14142019-04-10 15:22:09 -070043constexpr const bool DEBUG = false;
44
James Feist3cb5fec2018-01-23 14:41:51 -080045struct cmp_str
46{
James Feista465ccc2019-02-08 12:51:01 -080047 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080048 {
49 return std::strcmp(a, b) < 0;
50 }
51};
52
James Feist8f2710a2018-05-09 17:18:55 -070053struct PerformProbe;
54
James Feist3cb5fec2018-01-23 14:41:51 -080055// underscore T for collison with dbus c api
56enum class probe_type_codes
57{
58 FALSE_T,
59 TRUE_T,
60 AND,
61 OR,
James Feist6bd2a022018-03-13 12:30:58 -070062 FOUND,
63 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080064};
James Feista465ccc2019-02-08 12:51:01 -080065const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080066 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
67 {"TRUE", probe_type_codes::TRUE_T},
68 {"AND", probe_type_codes::AND},
69 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070070 {"FOUND", probe_type_codes::FOUND},
71 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080072
James Feist41334262019-03-25 13:30:20 -070073static constexpr std::array<const char*, 5> settableInterfaces = {
74 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds"};
James Feist68500ff2018-08-08 15:40:42 -070075using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080076 std::variant<std::vector<std::string>, std::vector<double>, std::string,
77 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
78 uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070079using BasicVariantType =
James Feista465ccc2019-02-08 12:51:01 -080080 std::variant<std::string, int64_t, uint64_t, double, int32_t, uint32_t,
81 int16_t, uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070082
James Feist3cb5fec2018-01-23 14:41:51 -080083using GetSubTreeType = std::vector<
84 std::pair<std::string,
85 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
86
87using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070088 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080089 boost::container::flat_map<
90 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070091 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080092
93boost::container::flat_map<
94 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070095 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -080096 DBUS_PROBE_OBJECTS;
97std::vector<std::string> PASSED_PROBES;
98
99// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700100std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist3cb5fec2018-01-23 14:41:51 -0800101
Johnathan Mantey2015f752019-03-26 15:22:31 -0700102const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
103const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800104
James Feista465ccc2019-02-08 12:51:01 -0800105void registerCallbacks(boost::asio::io_service& io,
106 std::vector<sdbusplus::bus::match::match>& dbusMatches,
107 nlohmann::json& systemConfiguration,
108 sdbusplus::asio::object_server& objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800109
James Feist3cb5fec2018-01-23 14:41:51 -0800110// calls the mapper to find all exposed objects of an interface type
111// and creates a vector<flat_map> that contains all the key value pairs
112// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700113void findDbusObjects(std::shared_ptr<PerformProbe> probe,
114 std::shared_ptr<sdbusplus::asio::connection> connection,
James Feista465ccc2019-02-08 12:51:01 -0800115 std::string& interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800116{
James Feist8f2710a2018-05-09 17:18:55 -0700117
118 // store reference to pending callbacks so we don't overwhelm services
119 static boost::container::flat_map<
120 std::string, std::vector<std::shared_ptr<PerformProbe>>>
121 pendingProbes;
122
123 if (DBUS_PROBE_OBJECTS[interface].size())
124 {
125 return;
126 }
127
128 // add shared_ptr to vector of Probes waiting for callback from a specific
129 // interface to keep alive while waiting for response
James Feista465ccc2019-02-08 12:51:01 -0800130 std::array<const char*, 1> objects = {interface.c_str()};
131 std::vector<std::shared_ptr<PerformProbe>>& pending =
James Feist8f2710a2018-05-09 17:18:55 -0700132 pendingProbes[interface];
133 auto iter = pending.emplace(pending.end(), probe);
134 // only allow first call to run to not overwhelm processes
135 if (iter != pending.begin())
136 {
137 return;
138 }
139
James Feist3cb5fec2018-01-23 14:41:51 -0800140 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700141 connection->async_method_call(
James Feista465ccc2019-02-08 12:51:01 -0800142 [connection, interface, probe](boost::system::error_code& ec,
143 const GetSubTreeType& interfaceSubtree) {
James Feist0de40152018-07-25 11:56:12 -0700144 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700145 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700146 {
James Feist0de40152018-07-25 11:56:12 -0700147 pendingProbes[interface].clear();
148 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700149 {
James Feist0de40152018-07-25 11:56:12 -0700150 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700151 }
James Feist0de40152018-07-25 11:56:12 -0700152 std::cerr << "Error communicating to mapper.\n";
153
154 // if we can't communicate to the mapper something is very wrong
155 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700156 }
James Feist8f2710a2018-05-09 17:18:55 -0700157 else
James Feist3cb5fec2018-01-23 14:41:51 -0800158 {
James Feista465ccc2019-02-08 12:51:01 -0800159 for (auto& object : interfaceSubtree)
James Feist8f2710a2018-05-09 17:18:55 -0700160 {
James Feista465ccc2019-02-08 12:51:01 -0800161 for (auto& connPair : object.second)
James Feist8f2710a2018-05-09 17:18:55 -0700162 {
James Feist0eb40352019-04-09 14:44:04 -0700163 interfaceConnections.insert(connPair.first);
James Feist8f2710a2018-05-09 17:18:55 -0700164 }
165 }
James Feist3cb5fec2018-01-23 14:41:51 -0800166 }
James Feist63845bf2019-01-24 12:19:51 -0800167 if (interfaceConnections.empty())
168 {
169 pendingProbes[interface].clear();
170 return;
171 }
James Feist8f2710a2018-05-09 17:18:55 -0700172 // get managed objects for all interfaces
James Feista465ccc2019-02-08 12:51:01 -0800173 for (const auto& conn : interfaceConnections)
James Feist8f2710a2018-05-09 17:18:55 -0700174 {
175 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700176 [conn,
James Feista465ccc2019-02-08 12:51:01 -0800177 interface](boost::system::error_code& ec,
178 const ManagedObjectType& managedInterface) {
James Feist8f2710a2018-05-09 17:18:55 -0700179 if (ec)
180 {
181 std::cerr
182 << "error getting managed object for device "
183 << conn << "\n";
184 pendingProbes[interface].clear();
185 return;
186 }
James Feista465ccc2019-02-08 12:51:01 -0800187 for (auto& interfaceManagedObj : managedInterface)
James Feist8f2710a2018-05-09 17:18:55 -0700188 {
189 auto ifaceObjFind =
190 interfaceManagedObj.second.find(interface);
191 if (ifaceObjFind !=
192 interfaceManagedObj.second.end())
193 {
194 std::vector<boost::container::flat_map<
James Feista465ccc2019-02-08 12:51:01 -0800195 std::string, BasicVariantType>>&
196 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist8f2710a2018-05-09 17:18:55 -0700197 dbusObject.emplace_back(ifaceObjFind->second);
198 }
199 }
200 pendingProbes[interface].clear();
201 },
202 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
203 "GetManagedObjects");
204 }
205 },
206 "xyz.openbmc_project.ObjectMapper",
207 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700208 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist8f2710a2018-05-09 17:18:55 -0700209 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800210}
James Feist8f2710a2018-05-09 17:18:55 -0700211// probes dbus interface dictionary for a key with a value that matches a regex
James Feist3cb5fec2018-01-23 14:41:51 -0800212bool probeDbus(
James Feista465ccc2019-02-08 12:51:01 -0800213 const std::string& interface,
214 const std::map<std::string, nlohmann::json>& matches,
James Feistf1b14142019-04-10 15:22:09 -0700215 std::vector<std::optional<
216 boost::container::flat_map<std::string, BasicVariantType>>>& devices,
James Feista465ccc2019-02-08 12:51:01 -0800217 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800218{
James Feista465ccc2019-02-08 12:51:01 -0800219 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
220 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800221 if (dbusObject.empty())
222 {
James Feist8f2710a2018-05-09 17:18:55 -0700223 foundProbe = false;
224 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800225 }
226 foundProbe = true;
227
228 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800229 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800230 {
231 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800232 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800233 {
234 auto deviceValue = device.find(match.first);
235 if (deviceValue != device.end())
236 {
237 switch (match.second.type())
238 {
James Feist9eb0b582018-04-27 12:15:46 -0700239 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800240 {
James Feist9eb0b582018-04-27 12:15:46 -0700241 std::regex search(match.second.get<std::string>());
242 std::smatch match;
243
244 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800245 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700246 VariantToStringVisitor(), deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700247 if (!std::regex_search(probeValue, match, search))
248 {
249 deviceMatches = false;
250 break;
251 }
James Feist3cb5fec2018-01-23 14:41:51 -0800252 break;
253 }
James Feist9eb0b582018-04-27 12:15:46 -0700254 case nlohmann::json::value_t::boolean:
255 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800256 {
James Feista465ccc2019-02-08 12:51:01 -0800257 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700258 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800259
James Feist9eb0b582018-04-27 12:15:46 -0700260 if (probeValue != match.second.get<unsigned int>())
261 {
262 deviceMatches = false;
263 }
264 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800265 }
James Feist9eb0b582018-04-27 12:15:46 -0700266 case nlohmann::json::value_t::number_integer:
267 {
James Feista465ccc2019-02-08 12:51:01 -0800268 int probeValue = std::visit(VariantToIntVisitor(),
269 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800270
James Feist9eb0b582018-04-27 12:15:46 -0700271 if (probeValue != match.second.get<int>())
272 {
273 deviceMatches = false;
274 }
275 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800276 }
James Feist9eb0b582018-04-27 12:15:46 -0700277 case nlohmann::json::value_t::number_float:
278 {
James Feista465ccc2019-02-08 12:51:01 -0800279 float probeValue = std::visit(VariantToFloatVisitor(),
280 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700281
282 if (probeValue != match.second.get<float>())
283 {
284 deviceMatches = false;
285 }
286 break;
287 }
James Feist0eb40352019-04-09 14:44:04 -0700288 default:
289 {
290 std::cerr << "unexpected dbus probe type "
291 << match.second.type_name() << "\n";
292 }
James Feist3cb5fec2018-01-23 14:41:51 -0800293 }
294 }
295 else
296 {
297 deviceMatches = false;
298 break;
299 }
300 }
301 if (deviceMatches)
302 {
303 devices.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700304 boost::container::flat_map<std::string, BasicVariantType>(
James Feist3cb5fec2018-01-23 14:41:51 -0800305 device));
306 foundMatch = true;
307 deviceMatches = false; // for next iteration
308 }
309 }
310 return foundMatch;
311}
312
313// default probe entry point, iterates a list looking for specific types to
314// call specific probe functions
315bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800316 const std::vector<std::string>& probeCommand,
James Feistf1b14142019-04-10 15:22:09 -0700317 std::vector<std::optional<
318 boost::container::flat_map<std::string, BasicVariantType>>>& foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800319{
320 const static std::regex command(R"(\((.*)\))");
321 std::smatch match;
322 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700323 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800324 bool cur = true;
325 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
326
James Feista465ccc2019-02-08 12:51:01 -0800327 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800328 {
329 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800330 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800331 cmp_str>::const_iterator probeType;
332
333 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
334 probeType++)
335 {
336 if (probe.find(probeType->first) != std::string::npos)
337 {
338 foundProbe = true;
339 break;
340 }
341 }
342 if (foundProbe)
343 {
344 switch (probeType->second)
345 {
James Feist9eb0b582018-04-27 12:15:46 -0700346 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800347 {
James Feist8f2710a2018-05-09 17:18:55 -0700348 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800349 }
James Feist9eb0b582018-04-27 12:15:46 -0700350 case probe_type_codes::TRUE_T:
351 {
James Feist8f2710a2018-05-09 17:18:55 -0700352 return true;
James Feist9eb0b582018-04-27 12:15:46 -0700353 }
354 case probe_type_codes::MATCH_ONE:
355 {
356 // set current value to last, this probe type shouldn't
357 // affect the outcome
358 cur = ret;
359 matchOne = true;
360 break;
361 }
362 /*case probe_type_codes::AND:
363 break;
364 case probe_type_codes::OR:
365 break;
366 // these are no-ops until the last command switch
367 */
368 case probe_type_codes::FOUND:
369 {
370 if (!std::regex_search(probe, match, command))
371 {
James Feist0eb40352019-04-09 14:44:04 -0700372 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700373 << "\n";
374 return false;
375 }
376 std::string commandStr = *(match.begin() + 1);
377 boost::replace_all(commandStr, "'", "");
378 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
379 commandStr) != PASSED_PROBES.end());
380 break;
381 }
James Feist0eb40352019-04-09 14:44:04 -0700382 default:
383 {
384 break;
385 }
James Feist3cb5fec2018-01-23 14:41:51 -0800386 }
387 }
388 // look on dbus for object
389 else
390 {
391 if (!std::regex_search(probe, match, command))
392 {
James Feist0eb40352019-04-09 14:44:04 -0700393 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800394 return false;
395 }
396 std::string commandStr = *(match.begin() + 1);
397 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700398 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800399 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800400 auto json = nlohmann::json::parse(commandStr, nullptr, false);
401 if (json.is_discarded())
402 {
James Feist0eb40352019-04-09 14:44:04 -0700403 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800404 return false;
405 }
406 // we can match any (string, variant) property. (string, string)
407 // does a regex
408 std::map<std::string, nlohmann::json> dbusProbeMap =
409 json.get<std::map<std::string, nlohmann::json>>();
410 auto findStart = probe.find("(");
411 if (findStart == std::string::npos)
412 {
413 return false;
414 }
415 std::string probeInterface = probe.substr(0, findStart);
416 cur =
417 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
418 }
419
420 // some functions like AND and OR only take affect after the
421 // fact
422 switch (lastCommand)
423 {
James Feist9eb0b582018-04-27 12:15:46 -0700424 case probe_type_codes::AND:
425 ret = cur && ret;
426 break;
427 case probe_type_codes::OR:
428 ret = cur || ret;
429 break;
430 default:
431 ret = cur;
432 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800433 }
434 lastCommand = probeType != PROBE_TYPES.end()
435 ? probeType->second
436 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800437 }
438
439 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800440 if (ret && foundDevs.size() == 0)
441 {
James Feistf1b14142019-04-10 15:22:09 -0700442 foundDevs.emplace_back(std::nullopt);
James Feist3cb5fec2018-01-23 14:41:51 -0800443 }
James Feist0eb40352019-04-09 14:44:04 -0700444 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700445 {
James Feist0eb40352019-04-09 14:44:04 -0700446 // match one could match multiple dbus values, which means we don't care
447 // what one we found so we shouldn't be using template replace. return
448 // an empty one
449 foundDevs.clear();
James Feistf1b14142019-04-10 15:22:09 -0700450 foundDevs.emplace_back(std::nullopt);
James Feist6bd2a022018-03-13 12:30:58 -0700451 }
James Feist3cb5fec2018-01-23 14:41:51 -0800452 return ret;
453}
James Feist8f2710a2018-05-09 17:18:55 -0700454// this class finds the needed dbus fields and on destruction runs the probe
455struct PerformProbe : std::enable_shared_from_this<PerformProbe>
456{
James Feist3cb5fec2018-01-23 14:41:51 -0800457
James Feist8f2710a2018-05-09 17:18:55 -0700458 PerformProbe(
James Feista465ccc2019-02-08 12:51:01 -0800459 const std::vector<std::string>& probeCommand,
James Feistf1b14142019-04-10 15:22:09 -0700460 std::function<void(std::vector<std::optional<boost::container::flat_map<
461 std::string, BasicVariantType>>>&)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -0700462 _probeCommand(probeCommand),
463 _callback(std::move(callback))
464 {
465 }
466 ~PerformProbe()
467 {
James Feistf1b14142019-04-10 15:22:09 -0700468 std::vector<std::optional<
469 boost::container::flat_map<std::string, BasicVariantType>>>
James Feist0eb40352019-04-09 14:44:04 -0700470 foundDevs;
471 if (probe(_probeCommand, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700472 {
James Feist0eb40352019-04-09 14:44:04 -0700473 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700474 }
475 }
476 void run()
477 {
478 // parse out dbus probes by discarding other probe types
James Feista465ccc2019-02-08 12:51:01 -0800479 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700480 cmp_str>::const_iterator probeType;
481
James Feista465ccc2019-02-08 12:51:01 -0800482 for (std::string& probe : _probeCommand)
James Feist8f2710a2018-05-09 17:18:55 -0700483 {
484 bool found = false;
James Feista465ccc2019-02-08 12:51:01 -0800485 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700486 cmp_str>::const_iterator probeType;
487 for (probeType = PROBE_TYPES.begin();
488 probeType != PROBE_TYPES.end(); probeType++)
489 {
490 if (probe.find(probeType->first) != std::string::npos)
491 {
492 found = true;
493 break;
494 }
495 }
496 if (found)
497 {
498 continue;
499 }
500 // syntax requires probe before first open brace
501 auto findStart = probe.find("(");
502 std::string interface = probe.substr(0, findStart);
503
504 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
505 }
506 }
507 std::vector<std::string> _probeCommand;
James Feistf1b14142019-04-10 15:22:09 -0700508 std::function<void(std::vector<std::optional<boost::container::flat_map<
509 std::string, BasicVariantType>>>&)>
James Feist8f2710a2018-05-09 17:18:55 -0700510 _callback;
James Feist8f2710a2018-05-09 17:18:55 -0700511};
512
513// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800514bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800515{
Ed Tanous072e25d2018-12-16 21:45:20 -0800516 std::filesystem::create_directory(OUTPUT_DIR);
James Feist1b2e2242018-01-30 13:45:19 -0800517 std::ofstream output(std::string(OUTPUT_DIR) + "system.json");
James Feistbb43d022018-06-12 15:44:33 -0700518 if (!output.good())
519 {
520 return false;
521 }
James Feist1b2e2242018-01-30 13:45:19 -0800522 output << systemConfiguration.dump(4);
523 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700524 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700525}
James Feist1b2e2242018-01-30 13:45:19 -0800526
James Feist97a63f12018-05-17 13:50:57 -0700527template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800528bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
529 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700530{
531 try
532 {
533 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800534 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700535 ref = value;
536 return true;
537 }
538 catch (const std::out_of_range)
539 {
540 return false;
541 }
542}
James Feistbb43d022018-06-12 15:44:33 -0700543
James Feistebcc26b2019-03-22 12:30:43 -0700544// template function to add array as dbus property
545template <typename PropertyType>
546void addArrayToDbus(const std::string& name, const nlohmann::json& array,
547 sdbusplus::asio::dbus_interface* iface,
548 sdbusplus::asio::PropertyPermission permission,
549 nlohmann::json& systemConfiguration,
550 const std::string& jsonPointerString)
551{
552 std::vector<PropertyType> values;
553 for (const auto& property : array)
554 {
555 auto ptr = property.get_ptr<const PropertyType*>();
556 if (ptr != nullptr)
557 {
558 values.emplace_back(*ptr);
559 }
560 }
561
562 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
563 {
564 iface->register_property(name, values);
565 }
566 else
567 {
568 iface->register_property(
569 name, values,
570 [&systemConfiguration,
571 jsonPointerString{std::string(jsonPointerString)}](
572 const std::vector<PropertyType>& newVal,
573 std::vector<PropertyType>& val) {
574 val = newVal;
575 if (!setJsonFromPointer(jsonPointerString, val,
576 systemConfiguration))
577 {
578 std::cerr << "error setting json field\n";
579 return -1;
580 }
581 if (!writeJsonFiles(systemConfiguration))
582 {
583 std::cerr << "error setting json file\n";
584 return -1;
585 }
586 return 1;
587 });
588 }
589}
590
James Feistbb43d022018-06-12 15:44:33 -0700591template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800592void addProperty(const std::string& propertyName, const PropertyType& value,
593 sdbusplus::asio::dbus_interface* iface,
594 nlohmann::json& systemConfiguration,
595 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700596 sdbusplus::asio::PropertyPermission permission)
597{
598 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
599 {
600 iface->register_property(propertyName, value);
601 return;
602 }
James Feist68500ff2018-08-08 15:40:42 -0700603 iface->register_property(
604 propertyName, value,
605 [&systemConfiguration,
606 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800607 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700608 val = newVal;
609 if (!setJsonFromPointer(jsonPointerString, val,
610 systemConfiguration))
611 {
612 std::cerr << "error setting json field\n";
613 return -1;
614 }
James Feistc6248a52018-08-14 10:09:45 -0700615 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700616 {
617 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700618 return -1;
619 }
620 return 1;
621 });
622}
623
624void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800625 const std::string& jsonPointerPath,
626 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
627 sdbusplus::asio::object_server& objServer,
628 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700629{
630 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
631 iface->register_method(
632 "Delete", [&objServer, &systemConfiguration, interface,
633 jsonPointerPath{std::string(jsonPointerPath)}]() {
634 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
635 interface.lock();
636 if (!iface)
637 {
638 // this technically can't happen as the pointer is pointing to
639 // us
640 throw DBusInternalError();
641 }
642 nlohmann::json::json_pointer ptr(jsonPointerPath);
643 if (!objServer.remove_interface(iface))
644 {
645 std::cerr << "Can't delete interface " << jsonPointerPath
646 << "\n";
647 throw DBusInternalError();
648 }
649 systemConfiguration[ptr] = nullptr;
650
651 if (!writeJsonFiles(systemConfiguration))
652 {
653 std::cerr << "error setting json file\n";
654 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700655 }
James Feistbb43d022018-06-12 15:44:33 -0700656 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700657 });
James Feistbb43d022018-06-12 15:44:33 -0700658}
659
James Feist1b2e2242018-01-30 13:45:19 -0800660// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700661void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800662 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
663 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
664 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700665 sdbusplus::asio::PropertyPermission permission =
666 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800667{
James Feista465ccc2019-02-08 12:51:01 -0800668 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800669 {
James Feist8f2710a2018-05-09 17:18:55 -0700670 auto type = dictPair.value().type();
671 bool array = false;
672 if (dictPair.value().type() == nlohmann::json::value_t::array)
673 {
674 array = true;
675 if (!dictPair.value().size())
676 {
677 continue;
678 }
679 type = dictPair.value()[0].type();
680 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800681 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700682 {
683 if (arrayItem.type() != type)
684 {
685 isLegal = false;
686 break;
687 }
688 }
689 if (!isLegal)
690 {
691 std::cerr << "dbus format error" << dictPair.value() << "\n";
692 continue;
693 }
James Feista218ddb2019-04-11 14:01:31 -0700694 }
695 if (type == nlohmann::json::value_t::object)
696 {
697 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700698 }
James Feist97a63f12018-05-17 13:50:57 -0700699 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700700 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
701 {
702 // all setable numbers are doubles as it is difficult to always
703 // create a configuration file with all whole numbers as decimals
704 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700705 if (array)
706 {
707 if (dictPair.value()[0].is_number())
708 {
709 type = nlohmann::json::value_t::number_float;
710 }
711 }
712 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700713 {
714 type = nlohmann::json::value_t::number_float;
715 }
716 }
717
James Feist8f2710a2018-05-09 17:18:55 -0700718 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800719 {
James Feist9eb0b582018-04-27 12:15:46 -0700720 case (nlohmann::json::value_t::boolean):
721 {
James Feist8f2710a2018-05-09 17:18:55 -0700722 if (array)
723 {
724 // todo: array of bool isn't detected correctly by
725 // sdbusplus, change it to numbers
726 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700727 iface.get(), permission,
728 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700729 }
James Feistbb43d022018-06-12 15:44:33 -0700730
James Feist97a63f12018-05-17 13:50:57 -0700731 else
732 {
James Feistbb43d022018-06-12 15:44:33 -0700733 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700734 iface.get(), systemConfiguration, key,
735 permission);
James Feist97a63f12018-05-17 13:50:57 -0700736 }
James Feist9eb0b582018-04-27 12:15:46 -0700737 break;
738 }
739 case (nlohmann::json::value_t::number_integer):
740 {
James Feist8f2710a2018-05-09 17:18:55 -0700741 if (array)
742 {
743 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700744 iface.get(), permission,
745 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700746 }
747 else
748 {
James Feistbb43d022018-06-12 15:44:33 -0700749 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700750 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700751 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700752 }
James Feist9eb0b582018-04-27 12:15:46 -0700753 break;
754 }
755 case (nlohmann::json::value_t::number_unsigned):
756 {
James Feist8f2710a2018-05-09 17:18:55 -0700757 if (array)
758 {
759 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700760 iface.get(), permission,
761 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700762 }
763 else
764 {
James Feistbb43d022018-06-12 15:44:33 -0700765 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700766 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700767 systemConfiguration, key,
768 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700769 }
James Feist9eb0b582018-04-27 12:15:46 -0700770 break;
771 }
772 case (nlohmann::json::value_t::number_float):
773 {
James Feist8f2710a2018-05-09 17:18:55 -0700774 if (array)
775 {
776 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700777 iface.get(), permission,
778 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700779 }
James Feistbb43d022018-06-12 15:44:33 -0700780
James Feist97a63f12018-05-17 13:50:57 -0700781 else
782 {
James Feistbb43d022018-06-12 15:44:33 -0700783 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700784 iface.get(), systemConfiguration, key,
785 permission);
James Feist97a63f12018-05-17 13:50:57 -0700786 }
James Feist9eb0b582018-04-27 12:15:46 -0700787 break;
788 }
789 case (nlohmann::json::value_t::string):
790 {
James Feist8f2710a2018-05-09 17:18:55 -0700791 if (array)
792 {
James Feistebcc26b2019-03-22 12:30:43 -0700793 addArrayToDbus<std::string>(
794 dictPair.key(), dictPair.value(), iface.get(),
795 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700796 }
797 else
798 {
James Feistc6248a52018-08-14 10:09:45 -0700799 addProperty(
800 dictPair.key(), dictPair.value().get<std::string>(),
801 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700802 }
James Feist9eb0b582018-04-27 12:15:46 -0700803 break;
804 }
James Feist0eb40352019-04-09 14:44:04 -0700805 default:
806 {
James Feista218ddb2019-04-11 14:01:31 -0700807 std::cerr << "Unexpected json type in system configuration "
808 << dictPair.key() << ": "
809 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700810 break;
811 }
James Feist1b2e2242018-01-30 13:45:19 -0800812 }
813 }
James Feistc6248a52018-08-14 10:09:45 -0700814 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
815 {
816 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
817 systemConfiguration);
818 }
James Feist8f2710a2018-05-09 17:18:55 -0700819 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800820}
821
James Feista465ccc2019-02-08 12:51:01 -0800822sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700823{
824 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
825 interface) != settableInterfaces.end()
826 ? sdbusplus::asio::PropertyPermission::readWrite
827 : sdbusplus::asio::PropertyPermission::readOnly;
828}
829
James Feista465ccc2019-02-08 12:51:01 -0800830void createAddObjectMethod(const std::string& jsonPointerPath,
831 const std::string& path,
832 nlohmann::json& systemConfiguration,
833 sdbusplus::asio::object_server& objServer)
James Feist68500ff2018-08-08 15:40:42 -0700834{
835 auto iface = objServer.add_interface(path, "xyz.openbmc_project.AddObject");
836
837 iface->register_method(
838 "AddObject",
839 [&systemConfiguration, &objServer,
840 jsonPointerPath{std::string(jsonPointerPath)},
841 path{std::string(path)}](
James Feista465ccc2019-02-08 12:51:01 -0800842 const boost::container::flat_map<std::string, JsonVariantType>&
843 data) {
James Feist68500ff2018-08-08 15:40:42 -0700844 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800845 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700846 auto findExposes = base.find("Exposes");
847
848 if (findExposes == base.end())
849 {
850 throw std::invalid_argument("Entity must have children.");
851 }
852
853 // this will throw invalid-argument to sdbusplus if invalid json
854 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800855 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700856 {
James Feista465ccc2019-02-08 12:51:01 -0800857 nlohmann::json& newJson = newData[item.first];
858 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
859 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700860 }
861
862 auto findName = newData.find("Name");
863 auto findType = newData.find("Type");
864 if (findName == newData.end() || findType == newData.end())
865 {
866 throw std::invalid_argument("AddObject missing Name or Type");
867 }
James Feista465ccc2019-02-08 12:51:01 -0800868 const std::string* type = findType->get_ptr<const std::string*>();
869 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700870 if (type == nullptr || name == nullptr)
871 {
872 throw std::invalid_argument("Type and Name must be a string.");
873 }
874
875 size_t lastIndex = 0;
876 // we add in the "exposes"
877 for (; lastIndex < findExposes->size(); lastIndex++)
878 {
879 if (findExposes->at(lastIndex)["Name"] == *name &&
880 findExposes->at(lastIndex)["Type"] == *type)
881 {
882 throw std::invalid_argument(
883 "Field already in JSON, not adding");
884 }
885 lastIndex++;
886 }
887
888 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
889 *type + ".json");
890 // todo(james) we might want to also make a list of 'can add'
891 // interfaces but for now I think the assumption if there is a
892 // schema avaliable that it is allowed to update is fine
893 if (!schemaFile.good())
894 {
895 throw std::invalid_argument(
896 "No schema avaliable, cannot validate.");
897 }
898 nlohmann::json schema =
899 nlohmann::json::parse(schemaFile, nullptr, false);
900 if (schema.is_discarded())
901 {
902 std::cerr << "Schema not legal" << *type << ".json\n";
903 throw DBusInternalError();
904 }
905 if (!validateJson(schema, newData))
906 {
907 throw std::invalid_argument("Data does not match schema");
908 }
909
910 if (!writeJsonFiles(systemConfiguration))
911 {
912 std::cerr << "Error writing json files\n";
913 throw DBusInternalError();
914 }
915 std::string dbusName = *name;
916
917 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700918 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist68500ff2018-08-08 15:40:42 -0700919 auto iface = objServer.add_interface(
920 path + "/" + dbusName,
921 "xyz.openbmc_project.Configuration." + *type);
922 // permission is read-write, as since we just created it, must be
923 // runtime modifiable
924 populateInterfaceFromJson(
925 systemConfiguration,
James Feistc6248a52018-08-14 10:09:45 -0700926 jsonPointerPath + "/" + std::to_string(lastIndex), iface,
James Feist68500ff2018-08-08 15:40:42 -0700927 newData, objServer,
928 sdbusplus::asio::PropertyPermission::readWrite);
929 // todo(james) generate patch
930 findExposes->push_back(newData);
931 });
932 iface->initialize();
933}
934
James Feista465ccc2019-02-08 12:51:01 -0800935void postToDbus(const nlohmann::json& newConfiguration,
936 nlohmann::json& systemConfiguration,
937 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800938
James Feist1b2e2242018-01-30 13:45:19 -0800939{
James Feist97a63f12018-05-17 13:50:57 -0700940 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800941 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800942 {
James Feistf1b14142019-04-10 15:22:09 -0700943 std::string boardKey = boardPair.value()["Name"];
944 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700945 // loop through newConfiguration, but use values from system
946 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700947 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700948 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800949 std::string boardType;
950 if (findBoardType != boardValues.end() &&
951 findBoardType->type() == nlohmann::json::value_t::string)
952 {
953 boardType = findBoardType->get<std::string>();
954 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700955 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800956 }
957 else
958 {
959 std::cerr << "Unable to find type for " << boardKey
960 << " reverting to Chassis.\n";
961 boardType = "Chassis";
962 }
James Feist11be6672018-04-06 14:05:32 -0700963 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800964
965 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700966 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700967 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
968 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800969
James Feist8f2710a2018-05-09 17:18:55 -0700970 auto inventoryIface = objServer.add_interface(
971 boardName, "xyz.openbmc_project.Inventory.Item");
James Feist68500ff2018-08-08 15:40:42 -0700972
James Feist8f2710a2018-05-09 17:18:55 -0700973 auto boardIface = objServer.add_interface(
974 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700975
James Feist68500ff2018-08-08 15:40:42 -0700976 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
977 objServer);
978
James Feist97a63f12018-05-17 13:50:57 -0700979 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700980 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700981 jsonPointerPath += "/";
982 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800983 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700984 {
985 if (boardField.value().type() == nlohmann::json::value_t::object)
986 {
James Feist8f2710a2018-05-09 17:18:55 -0700987 auto iface =
988 objServer.add_interface(boardName, boardField.key());
James Feistc6248a52018-08-14 10:09:45 -0700989 populateInterfaceFromJson(systemConfiguration,
990 jsonPointerPath + boardField.key(),
991 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700992 }
993 }
James Feist97a63f12018-05-17 13:50:57 -0700994
James Feist1e3e6982018-08-03 16:09:28 -0700995 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800996 if (exposes == boardValues.end())
997 {
998 continue;
999 }
James Feist97a63f12018-05-17 13:50:57 -07001000 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001001 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001002
1003 // store the board level pointer so we can modify it on the way down
1004 std::string jsonPointerPathBoard = jsonPointerPath;
1005 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001006 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001007 {
James Feist97a63f12018-05-17 13:50:57 -07001008 exposesIndex++;
1009 jsonPointerPath = jsonPointerPathBoard;
1010 jsonPointerPath += std::to_string(exposesIndex);
1011
James Feistd63d18a2018-07-19 15:23:45 -07001012 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001013 if (findName == item.end())
1014 {
1015 std::cerr << "cannot find name in field " << item << "\n";
1016 continue;
1017 }
James Feist1e3e6982018-08-03 16:09:28 -07001018 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001019 // if status is not found it is assumed to be status = 'okay'
1020 if (findStatus != item.end())
1021 {
1022 if (*findStatus == "disabled")
1023 {
1024 continue;
1025 }
1026 }
James Feistd63d18a2018-07-19 15:23:45 -07001027 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001028 std::string itemType;
1029 if (findType != item.end())
1030 {
1031 itemType = findType->get<std::string>();
1032 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001033 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1034 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001035 }
1036 else
1037 {
1038 itemType = "unknown";
1039 }
1040 std::string itemName = findName->get<std::string>();
1041 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001042 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001043
James Feist8f2710a2018-05-09 17:18:55 -07001044 auto itemIface = objServer.add_interface(
1045 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001046 "xyz.openbmc_project.Configuration." + itemType);
1047
James Feist97a63f12018-05-17 13:50:57 -07001048 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001049 itemIface, item, objServer,
1050 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001051
James Feista465ccc2019-02-08 12:51:01 -08001052 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001053 {
James Feist97a63f12018-05-17 13:50:57 -07001054 jsonPointerPath = jsonPointerPathBoard +
1055 std::to_string(exposesIndex) + "/" +
1056 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001057 if (objectPair.value().type() ==
1058 nlohmann::json::value_t::object)
1059 {
James Feist8f2710a2018-05-09 17:18:55 -07001060 auto objectIface = objServer.add_interface(
1061 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001062 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -07001063 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -07001064
1065 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001066 systemConfiguration, jsonPointerPath, objectIface,
1067 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001068 }
1069 else if (objectPair.value().type() ==
1070 nlohmann::json::value_t::array)
1071 {
1072 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001073 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001074 {
James Feist8f2710a2018-05-09 17:18:55 -07001075 continue;
1076 }
1077 bool isLegal = true;
1078 auto type = objectPair.value()[0].type();
1079 if (type != nlohmann::json::value_t::object)
1080 {
1081 continue;
1082 }
1083
1084 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001085 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001086 {
1087 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001088 {
James Feist8f2710a2018-05-09 17:18:55 -07001089 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001090 break;
1091 }
James Feist8f2710a2018-05-09 17:18:55 -07001092 }
1093 if (!isLegal)
1094 {
1095 std::cerr << "dbus format error" << objectPair.value()
1096 << "\n";
1097 break;
1098 }
1099
James Feista465ccc2019-02-08 12:51:01 -08001100 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001101 {
James Feist97a63f12018-05-17 13:50:57 -07001102
James Feist8f2710a2018-05-09 17:18:55 -07001103 auto objectIface = objServer.add_interface(
1104 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001105 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -07001106 "." + objectPair.key() + std::to_string(index));
James Feistc6248a52018-08-14 10:09:45 -07001107 populateInterfaceFromJson(
1108 systemConfiguration,
1109 jsonPointerPath + "/" + std::to_string(index),
1110 objectIface, arrayItem, objServer,
1111 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001112 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001113 }
1114 }
1115 }
1116 }
1117 }
1118}
1119
1120// finds the template character (currently set to $) and replaces the value with
1121// the field found in a dbus object i.e. $ADDRESS would get populated with the
1122// ADDRESS field from a object on dbus
1123void templateCharReplace(
James Feista465ccc2019-02-08 12:51:01 -08001124 nlohmann::json::iterator& keyPair,
1125 const boost::container::flat_map<std::string, BasicVariantType>&
1126 foundDevice,
1127 size_t& foundDeviceIdx)
James Feist1b2e2242018-01-30 13:45:19 -08001128{
James Feist11be6672018-04-06 14:05:32 -07001129 if (keyPair.value().type() == nlohmann::json::value_t::object)
1130 {
1131 for (auto nextLayer = keyPair.value().begin();
1132 nextLayer != keyPair.value().end(); nextLayer++)
1133 {
1134 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
1135 }
1136 return;
1137 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001138
1139 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
1140 if (strPtr == nullptr)
James Feist1b2e2242018-01-30 13:45:19 -08001141 {
1142 return;
1143 }
1144
Ed Tanous12bc7932019-02-26 14:36:20 -08001145 boost::replace_all(*strPtr, "$index", std::to_string(foundDeviceIdx));
1146
Ed Tanous12bc7932019-02-26 14:36:20 -08001147 for (auto& foundDevicePair : foundDevice)
James Feist1b2e2242018-01-30 13:45:19 -08001148 {
Ed Tanous12bc7932019-02-26 14:36:20 -08001149 std::string templateName = "$" + foundDevicePair.first;
1150 if (boost::iequals(*strPtr, templateName))
James Feist1b2e2242018-01-30 13:45:19 -08001151 {
Ed Tanous12bc7932019-02-26 14:36:20 -08001152 std::visit([&](auto&& val) { keyPair.value() = val; },
1153 foundDevicePair.second);
1154 // We probably just invalidated the pointer above, so set it to null
1155 strPtr = nullptr;
1156 break;
James Feist1b2e2242018-01-30 13:45:19 -08001157 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001158
1159 std::string probeValue =
1160 std::visit(VariantToStringVisitor(), foundDevicePair.second);
1161 boost::replace_all(*strPtr, templateName, probeValue);
1162 }
1163
1164 strPtr = keyPair.value().get_ptr<std::string*>();
1165 if (strPtr == nullptr)
1166 {
1167 return;
James Feist1b2e2242018-01-30 13:45:19 -08001168 }
James Feistc6090822019-01-04 16:02:48 -08001169
1170 // convert hex numbers to ints
Ed Tanous12bc7932019-02-26 14:36:20 -08001171 if (boost::starts_with(*strPtr, "0x"))
James Feist28dc2da2018-10-15 14:47:42 -07001172 {
1173 try
1174 {
James Feistc6090822019-01-04 16:02:48 -08001175 size_t pos = 0;
Ed Tanous12bc7932019-02-26 14:36:20 -08001176 int64_t temp = std::stoul(*strPtr, &pos, 0);
1177 if (pos == strPtr->size())
James Feistc6090822019-01-04 16:02:48 -08001178 {
1179 keyPair.value() = static_cast<uint64_t>(temp);
1180 }
James Feist28dc2da2018-10-15 14:47:42 -07001181 }
1182 catch (std::invalid_argument)
1183 {
1184 }
James Feistc6090822019-01-04 16:02:48 -08001185 catch (std::out_of_range)
1186 {
1187 }
James Feist28dc2da2018-10-15 14:47:42 -07001188 }
James Feist1b2e2242018-01-30 13:45:19 -08001189}
1190
James Feist8f2710a2018-05-09 17:18:55 -07001191// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001192bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001193{
1194 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001195 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001196 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1197 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001198 {
1199 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001200 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001201 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001202 }
James Feistb4383f42018-08-06 16:54:10 -07001203
1204 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1205 globalSchema);
1206 if (!schemaStream.good())
1207 {
1208 std::cerr
1209 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1210 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001211 return false;
James Feistb4383f42018-08-06 16:54:10 -07001212 }
1213 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1214 if (schema.is_discarded())
1215 {
1216 std::cerr
1217 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1218 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001219 return false;
James Feistb4383f42018-08-06 16:54:10 -07001220 }
1221
James Feista465ccc2019-02-08 12:51:01 -08001222 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001223 {
1224 std::ifstream jsonStream(jsonPath.c_str());
1225 if (!jsonStream.good())
1226 {
1227 std::cerr << "unable to open " << jsonPath.string() << "\n";
1228 continue;
1229 }
1230 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1231 if (data.is_discarded())
1232 {
1233 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1234 continue;
1235 }
James Feist8da99192019-01-24 08:20:16 -08001236 /*
1237 * todo(james): reenable this once less things are in flight
1238 *
James Feistb4383f42018-08-06 16:54:10 -07001239 if (!validateJson(schema, data))
1240 {
1241 std::cerr << "Error validating " << jsonPath.string() << "\n";
1242 continue;
1243 }
James Feist8da99192019-01-24 08:20:16 -08001244 */
James Feistb4383f42018-08-06 16:54:10 -07001245
James Feist3cb5fec2018-01-23 14:41:51 -08001246 if (data.type() == nlohmann::json::value_t::array)
1247 {
James Feista465ccc2019-02-08 12:51:01 -08001248 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001249 {
1250 configurations.emplace_back(d);
1251 }
1252 }
1253 else
1254 {
1255 configurations.emplace_back(data);
1256 }
1257 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001258 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001259}
James Feist3cb5fec2018-01-23 14:41:51 -08001260
James Feist8f2710a2018-05-09 17:18:55 -07001261struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001262{
James Feist75fdeeb2018-02-20 14:26:16 -08001263
James Feista465ccc2019-02-08 12:51:01 -08001264 PerformScan(nlohmann::json& systemConfiguration,
1265 std::list<nlohmann::json>& configurations,
1266 std::function<void(void)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -07001267 _systemConfiguration(systemConfiguration),
1268 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001269 {
James Feist8f2710a2018-05-09 17:18:55 -07001270 }
1271 void run()
1272 {
1273 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001274 {
James Feist1e3e6982018-08-03 16:09:28 -07001275 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001276 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001277
James Feist1b2e2242018-01-30 13:45:19 -08001278 nlohmann::json probeCommand;
1279 // check for poorly formatted fields, probe must be an array
1280 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001281 {
1282 std::cerr << "configuration file missing probe:\n " << *it
1283 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001284 it = _configurations.erase(it);
1285 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001286 }
James Feist1b2e2242018-01-30 13:45:19 -08001287 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001288 {
1289 probeCommand = nlohmann::json::array();
1290 probeCommand.push_back(*findProbe);
1291 }
1292 else
1293 {
1294 probeCommand = *findProbe;
1295 }
James Feist1b2e2242018-01-30 13:45:19 -08001296
1297 if (findName == it->end())
1298 {
1299 std::cerr << "configuration file missing name:\n " << *it
1300 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001301 it = _configurations.erase(it);
1302 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001303 }
James Feistf1b14142019-04-10 15:22:09 -07001304 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001305
James Feistf1b14142019-04-10 15:22:09 -07001306 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
1307 probeName) != PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001308 {
James Feist8f2710a2018-05-09 17:18:55 -07001309 it = _configurations.erase(it);
1310 continue;
1311 }
James Feistf1b14142019-04-10 15:22:09 -07001312 nlohmann::json* recordPtr = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001313
James Feist8f2710a2018-05-09 17:18:55 -07001314 // store reference to this to children to makes sure we don't get
1315 // destroyed too early
1316 auto thisRef = shared_from_this();
1317 auto p = std::make_shared<PerformProbe>(
1318 probeCommand,
James Feistf1b14142019-04-10 15:22:09 -07001319 [&, recordPtr, probeName,
1320 thisRef](std::vector<std::optional<boost::container::flat_map<
1321 std::string, BasicVariantType>>>& foundDevices) {
James Feist8f2710a2018-05-09 17:18:55 -07001322 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001323
James Feistf1b14142019-04-10 15:22:09 -07001324 PASSED_PROBES.push_back(probeName);
James Feist8f2710a2018-05-09 17:18:55 -07001325 size_t foundDeviceIdx = 0;
1326
James Feistf1b14142019-04-10 15:22:09 -07001327 nlohmann::json record = *recordPtr;
James Feistbe5425f2018-06-08 10:30:55 -07001328
James Feista465ccc2019-02-08 12:51:01 -08001329 for (auto& foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001330 {
James Feistf1b14142019-04-10 15:22:09 -07001331 std::string recordName;
1332 size_t hash = 0;
1333 if (foundDevice)
James Feist3cb5fec2018-01-23 14:41:51 -08001334 {
James Feistf1b14142019-04-10 15:22:09 -07001335 // use an array so alphabetical order from the
1336 // flat_map is maintained
1337 auto device = nlohmann::json::array();
1338 for (auto& devPair : *foundDevice)
1339 {
1340 device.push_back(devPair.first);
1341 std::visit(
1342 [&device](auto&& v) {
1343 device.push_back(v);
1344 },
1345 devPair.second);
1346 }
1347 hash = std::hash<std::string>{}(probeName +
1348 device.dump());
1349 // hashes are hard to distinguish, use the
1350 // non-hashed version if we want debug
1351 if constexpr (DEBUG)
1352 {
1353 recordName = probeName + device.dump();
1354 }
1355 else
1356 {
1357 recordName = std::to_string(hash);
1358 }
James Feist8f2710a2018-05-09 17:18:55 -07001359 }
James Feistf1b14142019-04-10 15:22:09 -07001360 else
1361 {
1362 recordName = probeName;
1363 }
1364
1365 // insert into configuration temporarily to be able to
1366 // reference ourselves
1367
1368 _systemConfiguration[recordName] = record;
1369
1370 if (foundDevice)
1371 {
1372 for (auto keyPair = record.begin();
1373 keyPair != record.end(); keyPair++)
1374 {
1375 templateCharReplace(keyPair, *foundDevice,
1376 foundDeviceIdx);
1377 }
1378 }
1379 auto findExpose = record.find("Exposes");
1380 if (findExpose == record.end())
James Feist8f2710a2018-05-09 17:18:55 -07001381 {
1382 continue;
1383 }
James Feista465ccc2019-02-08 12:51:01 -08001384 for (auto& expose : *findExpose)
James Feist8f2710a2018-05-09 17:18:55 -07001385 {
1386 for (auto keyPair = expose.begin();
1387 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001388 {
James Feist1b2e2242018-01-30 13:45:19 -08001389
James Feist8f2710a2018-05-09 17:18:55 -07001390 // fill in template characters with devices
1391 // found
James Feistf1b14142019-04-10 15:22:09 -07001392 if (foundDevice)
1393 {
1394 templateCharReplace(keyPair, *foundDevice,
1395 foundDeviceIdx);
1396 }
James Feist8f2710a2018-05-09 17:18:55 -07001397 // special case bind
James Feist1e3e6982018-08-03 16:09:28 -07001398 if (boost::starts_with(keyPair.key(), "Bind"))
James Feist8f2710a2018-05-09 17:18:55 -07001399 {
1400 if (keyPair.value().type() !=
1401 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001402 {
James Feist8f2710a2018-05-09 17:18:55 -07001403 std::cerr << "bind_ value must be of "
1404 "type string "
1405 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001406 continue;
1407 }
James Feist8f2710a2018-05-09 17:18:55 -07001408 bool foundBind = false;
1409 std::string bind = keyPair.key().substr(
James Feist1e3e6982018-08-03 16:09:28 -07001410 sizeof("Bind") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001411
James Feista465ccc2019-02-08 12:51:01 -08001412 for (auto& configurationPair :
James Feist8f2710a2018-05-09 17:18:55 -07001413 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001414 {
James Feist1b2e2242018-01-30 13:45:19 -08001415
James Feist8f2710a2018-05-09 17:18:55 -07001416 auto configListFind =
1417 configurationPair.value().find(
James Feist1e3e6982018-08-03 16:09:28 -07001418 "Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001419
1420 if (configListFind ==
1421 configurationPair.value()
1422 .end() ||
1423 configListFind->type() !=
1424 nlohmann::json::value_t::array)
1425 {
1426 continue;
1427 }
James Feista465ccc2019-02-08 12:51:01 -08001428 for (auto& exposedObject :
James Feist8f2710a2018-05-09 17:18:55 -07001429 *configListFind)
1430 {
1431 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001432 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001433 if (boost::iequals(
1434 foundObjectName,
1435 keyPair.value()
1436 .get<std::string>()))
1437 {
James Feist1e3e6982018-08-03 16:09:28 -07001438 exposedObject["Status"] =
James Feist8f2710a2018-05-09 17:18:55 -07001439 "okay";
1440 expose[bind] = exposedObject;
1441
1442 foundBind = true;
1443 break;
1444 }
1445 }
1446 if (foundBind)
1447 {
James Feist3cb5fec2018-01-23 14:41:51 -08001448 break;
1449 }
1450 }
James Feist8f2710a2018-05-09 17:18:55 -07001451 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001452 {
James Feist8f2710a2018-05-09 17:18:55 -07001453 std::cerr << "configuration file "
1454 "dependency error, "
1455 "could not find bind "
1456 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001457 }
1458 }
1459 }
1460 }
James Feistf1b14142019-04-10 15:22:09 -07001461 // overwrite ourselves with cleaned up version
1462 _systemConfiguration[recordName] = record;
1463 foundDeviceIdx++;
James Feist3cb5fec2018-01-23 14:41:51 -08001464 }
James Feist8f2710a2018-05-09 17:18:55 -07001465 });
1466 p->run();
1467 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001468 }
1469 }
James Feist75fdeeb2018-02-20 14:26:16 -08001470
James Feist8f2710a2018-05-09 17:18:55 -07001471 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001472 {
James Feist8f2710a2018-05-09 17:18:55 -07001473 if (_passed)
1474 {
1475 auto nextScan = std::make_shared<PerformScan>(
1476 _systemConfiguration, _configurations, std::move(_callback));
1477 nextScan->run();
1478 }
1479 else
1480 {
1481 _callback();
1482 }
1483 }
James Feista465ccc2019-02-08 12:51:01 -08001484 nlohmann::json& _systemConfiguration;
James Feist8f2710a2018-05-09 17:18:55 -07001485 std::list<nlohmann::json> _configurations;
1486 std::function<void(void)> _callback;
1487 std::vector<std::shared_ptr<PerformProbe>> _probes;
1488 bool _passed = false;
1489};
James Feistc95cb142018-02-26 10:41:42 -08001490
James Feist8f2710a2018-05-09 17:18:55 -07001491// main properties changed entry
1492void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001493 boost::asio::io_service& io,
1494 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1495 nlohmann::json& systemConfiguration,
1496 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001497{
1498 static boost::asio::deadline_timer timer(io);
1499 timer.expires_from_now(boost::posix_time::seconds(1));
1500
1501 // setup an async wait as we normally get flooded with new requests
James Feista465ccc2019-02-08 12:51:01 -08001502 timer.async_wait([&](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001503 if (ec == boost::asio::error::operation_aborted)
1504 {
1505 // we were cancelled
1506 return;
1507 }
1508 else if (ec)
1509 {
1510 std::cerr << "async wait error " << ec << "\n";
1511 return;
1512 }
1513
1514 nlohmann::json oldConfiguration = systemConfiguration;
1515 DBUS_PROBE_OBJECTS.clear();
1516
1517 std::list<nlohmann::json> configurations;
1518 if (!findJsonFiles(configurations))
1519 {
1520 std::cerr << "cannot find json files\n";
1521 return;
1522 }
1523
1524 auto perfScan = std::make_shared<PerformScan>(
1525 systemConfiguration, configurations, [&, oldConfiguration]() {
1526 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001527 for (auto it = newConfiguration.begin();
1528 it != newConfiguration.end();)
1529 {
1530 auto findKey = oldConfiguration.find(it.key());
1531 if (findKey != oldConfiguration.end())
1532 {
1533 it = newConfiguration.erase(it);
1534 }
1535 else
1536 {
1537 it++;
1538 }
1539 }
James Feist8f2710a2018-05-09 17:18:55 -07001540 registerCallbacks(io, dbusMatches, systemConfiguration,
1541 objServer);
1542 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001543 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001544
James Feistbb43d022018-06-12 15:44:33 -07001545 io.post([&]() {
1546 if (!writeJsonFiles(systemConfiguration))
1547 {
1548 std::cerr << "Error writing json files\n";
1549 }
1550 });
James Feist8f2710a2018-05-09 17:18:55 -07001551 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001552 postToDbus(newConfiguration, systemConfiguration,
1553 objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001554 });
1555 });
1556 });
1557 perfScan->run();
1558 });
James Feist75fdeeb2018-02-20 14:26:16 -08001559}
1560
James Feista465ccc2019-02-08 12:51:01 -08001561void registerCallbacks(boost::asio::io_service& io,
1562 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1563 nlohmann::json& systemConfiguration,
1564 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001565{
1566 static boost::container::flat_set<std::string> watchedObjects;
1567
James Feista465ccc2019-02-08 12:51:01 -08001568 for (const auto& objectMap : DBUS_PROBE_OBJECTS)
James Feist75fdeeb2018-02-20 14:26:16 -08001569 {
1570 auto findObject = watchedObjects.find(objectMap.first);
1571 if (findObject != watchedObjects.end())
1572 {
1573 continue;
1574 }
James Feist8f2710a2018-05-09 17:18:55 -07001575 std::function<void(sdbusplus::message::message & message)>
1576 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001577
James Feista465ccc2019-02-08 12:51:01 -08001578 [&](sdbusplus::message::message&) {
James Feist8f2710a2018-05-09 17:18:55 -07001579 propertiesChangedCallback(io, dbusMatches,
1580 systemConfiguration, objServer);
1581 };
1582
1583 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001584 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001585 "type='signal',member='PropertiesChanged',arg0='" +
1586 objectMap.first + "'",
1587 eventHandler);
1588 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001589 }
1590}
1591
James Feista465ccc2019-02-08 12:51:01 -08001592int main(int argc, char** argv)
James Feist75fdeeb2018-02-20 14:26:16 -08001593{
1594 // setup connection to dbus
1595 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001596 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001597 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001598
James Feist8f2710a2018-05-09 17:18:55 -07001599 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001600
James Feist8f2710a2018-05-09 17:18:55 -07001601 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1602 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1603 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001604
James Feist8f2710a2018-05-09 17:18:55 -07001605 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1606 objServer.add_interface("/xyz/openbmc_project/inventory",
1607 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001608
1609 // to keep reference to the match / filter objects so they don't get
1610 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001611 std::vector<sdbusplus::bus::match::match> dbusMatches;
1612
1613 nlohmann::json systemConfiguration = nlohmann::json::object();
1614
1615 inventoryIface->register_method(
James Feista465ccc2019-02-08 12:51:01 -08001616 "Notify",
1617 [](const boost::container::flat_map<
1618 std::string,
1619 boost::container::flat_map<std::string, BasicVariantType>>&
1620 object) { return; });
James Feist8f2710a2018-05-09 17:18:55 -07001621 inventoryIface->initialize();
1622
1623 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001624#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001625 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001626#endif
James Feist8f2710a2018-05-09 17:18:55 -07001627 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1628 objServer);
1629 });
James Feist4131aea2018-03-09 09:47:30 -08001630
James Feistfd1264a2018-05-03 12:10:00 -07001631 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001632 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1633 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001634 });
James Feist8f2710a2018-05-09 17:18:55 -07001635 entityIface->initialize();
1636
James Feist1b2e2242018-01-30 13:45:19 -08001637 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001638
1639 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001640}