blob: fb75856018a6afe4b9b42a8b596102a9309c34c9 [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 Feist3cb5fec2018-01-23 14:41:51 -080043struct cmp_str
44{
James Feista465ccc2019-02-08 12:51:01 -080045 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080046 {
47 return std::strcmp(a, b) < 0;
48 }
49};
50
James Feist8f2710a2018-05-09 17:18:55 -070051struct PerformProbe;
52
James Feist3cb5fec2018-01-23 14:41:51 -080053// underscore T for collison with dbus c api
54enum class probe_type_codes
55{
56 FALSE_T,
57 TRUE_T,
58 AND,
59 OR,
James Feist6bd2a022018-03-13 12:30:58 -070060 FOUND,
61 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080062};
James Feista465ccc2019-02-08 12:51:01 -080063const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080064 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
65 {"TRUE", probe_type_codes::TRUE_T},
66 {"AND", probe_type_codes::AND},
67 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070068 {"FOUND", probe_type_codes::FOUND},
69 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080070
James Feist41334262019-03-25 13:30:20 -070071static constexpr std::array<const char*, 5> settableInterfaces = {
72 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds"};
James Feist68500ff2018-08-08 15:40:42 -070073using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080074 std::variant<std::vector<std::string>, std::vector<double>, std::string,
75 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
76 uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070077using BasicVariantType =
James Feista465ccc2019-02-08 12:51:01 -080078 std::variant<std::string, int64_t, uint64_t, double, int32_t, uint32_t,
79 int16_t, uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070080
James Feist3cb5fec2018-01-23 14:41:51 -080081using GetSubTreeType = std::vector<
82 std::pair<std::string,
83 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
84
85using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070086 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080087 boost::container::flat_map<
88 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070089 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080090
91boost::container::flat_map<
92 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070093 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -080094 DBUS_PROBE_OBJECTS;
95std::vector<std::string> PASSED_PROBES;
96
97// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -070098std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist3cb5fec2018-01-23 14:41:51 -080099
Johnathan Mantey2015f752019-03-26 15:22:31 -0700100const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
101const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800102
James Feista465ccc2019-02-08 12:51:01 -0800103void registerCallbacks(boost::asio::io_service& io,
104 std::vector<sdbusplus::bus::match::match>& dbusMatches,
105 nlohmann::json& systemConfiguration,
106 sdbusplus::asio::object_server& objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800107
James Feist3cb5fec2018-01-23 14:41:51 -0800108// calls the mapper to find all exposed objects of an interface type
109// and creates a vector<flat_map> that contains all the key value pairs
110// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700111void findDbusObjects(std::shared_ptr<PerformProbe> probe,
112 std::shared_ptr<sdbusplus::asio::connection> connection,
James Feista465ccc2019-02-08 12:51:01 -0800113 std::string& interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800114{
James Feist8f2710a2018-05-09 17:18:55 -0700115
116 // store reference to pending callbacks so we don't overwhelm services
117 static boost::container::flat_map<
118 std::string, std::vector<std::shared_ptr<PerformProbe>>>
119 pendingProbes;
120
121 if (DBUS_PROBE_OBJECTS[interface].size())
122 {
123 return;
124 }
125
126 // add shared_ptr to vector of Probes waiting for callback from a specific
127 // interface to keep alive while waiting for response
James Feista465ccc2019-02-08 12:51:01 -0800128 std::array<const char*, 1> objects = {interface.c_str()};
129 std::vector<std::shared_ptr<PerformProbe>>& pending =
James Feist8f2710a2018-05-09 17:18:55 -0700130 pendingProbes[interface];
131 auto iter = pending.emplace(pending.end(), probe);
132 // only allow first call to run to not overwhelm processes
133 if (iter != pending.begin())
134 {
135 return;
136 }
137
James Feist3cb5fec2018-01-23 14:41:51 -0800138 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700139 connection->async_method_call(
James Feista465ccc2019-02-08 12:51:01 -0800140 [connection, interface, probe](boost::system::error_code& ec,
141 const GetSubTreeType& interfaceSubtree) {
James Feist0de40152018-07-25 11:56:12 -0700142 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700143 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700144 {
James Feist0de40152018-07-25 11:56:12 -0700145 pendingProbes[interface].clear();
146 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700147 {
James Feist0de40152018-07-25 11:56:12 -0700148 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700149 }
James Feist0de40152018-07-25 11:56:12 -0700150 std::cerr << "Error communicating to mapper.\n";
151
152 // if we can't communicate to the mapper something is very wrong
153 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700154 }
James Feist8f2710a2018-05-09 17:18:55 -0700155 else
James Feist3cb5fec2018-01-23 14:41:51 -0800156 {
James Feista465ccc2019-02-08 12:51:01 -0800157 for (auto& object : interfaceSubtree)
James Feist8f2710a2018-05-09 17:18:55 -0700158 {
James Feista465ccc2019-02-08 12:51:01 -0800159 for (auto& connPair : object.second)
James Feist8f2710a2018-05-09 17:18:55 -0700160 {
James Feist0eb40352019-04-09 14:44:04 -0700161 interfaceConnections.insert(connPair.first);
James Feist8f2710a2018-05-09 17:18:55 -0700162 }
163 }
James Feist3cb5fec2018-01-23 14:41:51 -0800164 }
James Feist63845bf2019-01-24 12:19:51 -0800165 if (interfaceConnections.empty())
166 {
167 pendingProbes[interface].clear();
168 return;
169 }
James Feist8f2710a2018-05-09 17:18:55 -0700170 // get managed objects for all interfaces
James Feista465ccc2019-02-08 12:51:01 -0800171 for (const auto& conn : interfaceConnections)
James Feist8f2710a2018-05-09 17:18:55 -0700172 {
173 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700174 [conn,
James Feista465ccc2019-02-08 12:51:01 -0800175 interface](boost::system::error_code& ec,
176 const ManagedObjectType& managedInterface) {
James Feist8f2710a2018-05-09 17:18:55 -0700177 if (ec)
178 {
179 std::cerr
180 << "error getting managed object for device "
181 << conn << "\n";
182 pendingProbes[interface].clear();
183 return;
184 }
James Feista465ccc2019-02-08 12:51:01 -0800185 for (auto& interfaceManagedObj : managedInterface)
James Feist8f2710a2018-05-09 17:18:55 -0700186 {
187 auto ifaceObjFind =
188 interfaceManagedObj.second.find(interface);
189 if (ifaceObjFind !=
190 interfaceManagedObj.second.end())
191 {
192 std::vector<boost::container::flat_map<
James Feista465ccc2019-02-08 12:51:01 -0800193 std::string, BasicVariantType>>&
194 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist8f2710a2018-05-09 17:18:55 -0700195 dbusObject.emplace_back(ifaceObjFind->second);
196 }
197 }
198 pendingProbes[interface].clear();
199 },
200 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
201 "GetManagedObjects");
202 }
203 },
204 "xyz.openbmc_project.ObjectMapper",
205 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700206 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist8f2710a2018-05-09 17:18:55 -0700207 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800208}
James Feist8f2710a2018-05-09 17:18:55 -0700209// probes dbus interface dictionary for a key with a value that matches a regex
James Feist3cb5fec2018-01-23 14:41:51 -0800210bool probeDbus(
James Feista465ccc2019-02-08 12:51:01 -0800211 const std::string& interface,
212 const std::map<std::string, nlohmann::json>& matches,
213 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
214 devices,
215 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800216{
James Feista465ccc2019-02-08 12:51:01 -0800217 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
218 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800219 if (dbusObject.empty())
220 {
James Feist8f2710a2018-05-09 17:18:55 -0700221 foundProbe = false;
222 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800223 }
224 foundProbe = true;
225
226 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800227 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800228 {
229 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800230 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800231 {
232 auto deviceValue = device.find(match.first);
233 if (deviceValue != device.end())
234 {
235 switch (match.second.type())
236 {
James Feist9eb0b582018-04-27 12:15:46 -0700237 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800238 {
James Feist9eb0b582018-04-27 12:15:46 -0700239 std::regex search(match.second.get<std::string>());
240 std::smatch match;
241
242 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800243 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700244 VariantToStringVisitor(), deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700245 if (!std::regex_search(probeValue, match, search))
246 {
247 deviceMatches = false;
248 break;
249 }
James Feist3cb5fec2018-01-23 14:41:51 -0800250 break;
251 }
James Feist9eb0b582018-04-27 12:15:46 -0700252 case nlohmann::json::value_t::boolean:
253 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800254 {
James Feista465ccc2019-02-08 12:51:01 -0800255 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700256 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800257
James Feist9eb0b582018-04-27 12:15:46 -0700258 if (probeValue != match.second.get<unsigned int>())
259 {
260 deviceMatches = false;
261 }
262 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800263 }
James Feist9eb0b582018-04-27 12:15:46 -0700264 case nlohmann::json::value_t::number_integer:
265 {
James Feista465ccc2019-02-08 12:51:01 -0800266 int probeValue = std::visit(VariantToIntVisitor(),
267 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800268
James Feist9eb0b582018-04-27 12:15:46 -0700269 if (probeValue != match.second.get<int>())
270 {
271 deviceMatches = false;
272 }
273 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800274 }
James Feist9eb0b582018-04-27 12:15:46 -0700275 case nlohmann::json::value_t::number_float:
276 {
James Feista465ccc2019-02-08 12:51:01 -0800277 float probeValue = std::visit(VariantToFloatVisitor(),
278 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700279
280 if (probeValue != match.second.get<float>())
281 {
282 deviceMatches = false;
283 }
284 break;
285 }
James Feist0eb40352019-04-09 14:44:04 -0700286 default:
287 {
288 std::cerr << "unexpected dbus probe type "
289 << match.second.type_name() << "\n";
290 }
James Feist3cb5fec2018-01-23 14:41:51 -0800291 }
292 }
293 else
294 {
295 deviceMatches = false;
296 break;
297 }
298 }
299 if (deviceMatches)
300 {
301 devices.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700302 boost::container::flat_map<std::string, BasicVariantType>(
James Feist3cb5fec2018-01-23 14:41:51 -0800303 device));
304 foundMatch = true;
305 deviceMatches = false; // for next iteration
306 }
307 }
308 return foundMatch;
309}
310
311// default probe entry point, iterates a list looking for specific types to
312// call specific probe functions
313bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800314 const std::vector<std::string>& probeCommand,
315 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
316 foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800317{
318 const static std::regex command(R"(\((.*)\))");
319 std::smatch match;
320 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700321 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800322 bool cur = true;
323 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
324
James Feista465ccc2019-02-08 12:51:01 -0800325 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800326 {
327 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800328 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800329 cmp_str>::const_iterator probeType;
330
331 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
332 probeType++)
333 {
334 if (probe.find(probeType->first) != std::string::npos)
335 {
336 foundProbe = true;
337 break;
338 }
339 }
340 if (foundProbe)
341 {
342 switch (probeType->second)
343 {
James Feist9eb0b582018-04-27 12:15:46 -0700344 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800345 {
James Feist8f2710a2018-05-09 17:18:55 -0700346 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800347 }
James Feist9eb0b582018-04-27 12:15:46 -0700348 case probe_type_codes::TRUE_T:
349 {
James Feist8f2710a2018-05-09 17:18:55 -0700350 return true;
James Feist9eb0b582018-04-27 12:15:46 -0700351 }
352 case probe_type_codes::MATCH_ONE:
353 {
354 // set current value to last, this probe type shouldn't
355 // affect the outcome
356 cur = ret;
357 matchOne = true;
358 break;
359 }
360 /*case probe_type_codes::AND:
361 break;
362 case probe_type_codes::OR:
363 break;
364 // these are no-ops until the last command switch
365 */
366 case probe_type_codes::FOUND:
367 {
368 if (!std::regex_search(probe, match, command))
369 {
James Feist0eb40352019-04-09 14:44:04 -0700370 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700371 << "\n";
372 return false;
373 }
374 std::string commandStr = *(match.begin() + 1);
375 boost::replace_all(commandStr, "'", "");
376 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
377 commandStr) != PASSED_PROBES.end());
378 break;
379 }
James Feist0eb40352019-04-09 14:44:04 -0700380 default:
381 {
382 break;
383 }
James Feist3cb5fec2018-01-23 14:41:51 -0800384 }
385 }
386 // look on dbus for object
387 else
388 {
389 if (!std::regex_search(probe, match, command))
390 {
James Feist0eb40352019-04-09 14:44:04 -0700391 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800392 return false;
393 }
394 std::string commandStr = *(match.begin() + 1);
395 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700396 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800397 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800398 auto json = nlohmann::json::parse(commandStr, nullptr, false);
399 if (json.is_discarded())
400 {
James Feist0eb40352019-04-09 14:44:04 -0700401 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800402 return false;
403 }
404 // we can match any (string, variant) property. (string, string)
405 // does a regex
406 std::map<std::string, nlohmann::json> dbusProbeMap =
407 json.get<std::map<std::string, nlohmann::json>>();
408 auto findStart = probe.find("(");
409 if (findStart == std::string::npos)
410 {
411 return false;
412 }
413 std::string probeInterface = probe.substr(0, findStart);
414 cur =
415 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
416 }
417
418 // some functions like AND and OR only take affect after the
419 // fact
420 switch (lastCommand)
421 {
James Feist9eb0b582018-04-27 12:15:46 -0700422 case probe_type_codes::AND:
423 ret = cur && ret;
424 break;
425 case probe_type_codes::OR:
426 ret = cur || ret;
427 break;
428 default:
429 ret = cur;
430 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800431 }
432 lastCommand = probeType != PROBE_TYPES.end()
433 ? probeType->second
434 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800435 }
436
437 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800438 if (ret && foundDevs.size() == 0)
439 {
440 foundDevs.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700441 boost::container::flat_map<std::string, BasicVariantType>());
James Feist3cb5fec2018-01-23 14:41:51 -0800442 }
James Feist0eb40352019-04-09 14:44:04 -0700443 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700444 {
James Feist0eb40352019-04-09 14:44:04 -0700445 // match one could match multiple dbus values, which means we don't care
446 // what one we found so we shouldn't be using template replace. return
447 // an empty one
448 foundDevs.clear();
449 foundDevs.emplace_back(
450 boost::container::flat_map<std::string, BasicVariantType>());
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 Feist8f2710a2018-05-09 17:18:55 -0700460 std::function<void(std::vector<boost::container::flat_map<
James Feista465ccc2019-02-08 12:51:01 -0800461 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 Feist0eb40352019-04-09 14:44:04 -0700468 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
469 foundDevs;
470 if (probe(_probeCommand, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700471 {
James Feist0eb40352019-04-09 14:44:04 -0700472 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700473 }
474 }
475 void run()
476 {
477 // parse out dbus probes by discarding other probe types
James Feista465ccc2019-02-08 12:51:01 -0800478 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700479 cmp_str>::const_iterator probeType;
480
James Feista465ccc2019-02-08 12:51:01 -0800481 for (std::string& probe : _probeCommand)
James Feist8f2710a2018-05-09 17:18:55 -0700482 {
483 bool found = false;
James Feista465ccc2019-02-08 12:51:01 -0800484 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700485 cmp_str>::const_iterator probeType;
486 for (probeType = PROBE_TYPES.begin();
487 probeType != PROBE_TYPES.end(); probeType++)
488 {
489 if (probe.find(probeType->first) != std::string::npos)
490 {
491 found = true;
492 break;
493 }
494 }
495 if (found)
496 {
497 continue;
498 }
499 // syntax requires probe before first open brace
500 auto findStart = probe.find("(");
501 std::string interface = probe.substr(0, findStart);
502
503 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
504 }
505 }
506 std::vector<std::string> _probeCommand;
James Feista465ccc2019-02-08 12:51:01 -0800507 std::function<void(std::vector<boost::container::flat_map<
508 std::string, BasicVariantType>>&)>
James Feist8f2710a2018-05-09 17:18:55 -0700509 _callback;
James Feist8f2710a2018-05-09 17:18:55 -0700510};
511
512// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800513bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800514{
Ed Tanous072e25d2018-12-16 21:45:20 -0800515 std::filesystem::create_directory(OUTPUT_DIR);
James Feist1b2e2242018-01-30 13:45:19 -0800516 std::ofstream output(std::string(OUTPUT_DIR) + "system.json");
James Feistbb43d022018-06-12 15:44:33 -0700517 if (!output.good())
518 {
519 return false;
520 }
James Feist1b2e2242018-01-30 13:45:19 -0800521 output << systemConfiguration.dump(4);
522 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700523 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700524}
James Feist1b2e2242018-01-30 13:45:19 -0800525
James Feist97a63f12018-05-17 13:50:57 -0700526template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800527bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
528 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700529{
530 try
531 {
532 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800533 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700534 ref = value;
535 return true;
536 }
537 catch (const std::out_of_range)
538 {
539 return false;
540 }
541}
James Feistbb43d022018-06-12 15:44:33 -0700542
James Feistebcc26b2019-03-22 12:30:43 -0700543// template function to add array as dbus property
544template <typename PropertyType>
545void addArrayToDbus(const std::string& name, const nlohmann::json& array,
546 sdbusplus::asio::dbus_interface* iface,
547 sdbusplus::asio::PropertyPermission permission,
548 nlohmann::json& systemConfiguration,
549 const std::string& jsonPointerString)
550{
551 std::vector<PropertyType> values;
552 for (const auto& property : array)
553 {
554 auto ptr = property.get_ptr<const PropertyType*>();
555 if (ptr != nullptr)
556 {
557 values.emplace_back(*ptr);
558 }
559 }
560
561 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
562 {
563 iface->register_property(name, values);
564 }
565 else
566 {
567 iface->register_property(
568 name, values,
569 [&systemConfiguration,
570 jsonPointerString{std::string(jsonPointerString)}](
571 const std::vector<PropertyType>& newVal,
572 std::vector<PropertyType>& val) {
573 val = newVal;
574 if (!setJsonFromPointer(jsonPointerString, val,
575 systemConfiguration))
576 {
577 std::cerr << "error setting json field\n";
578 return -1;
579 }
580 if (!writeJsonFiles(systemConfiguration))
581 {
582 std::cerr << "error setting json file\n";
583 return -1;
584 }
585 return 1;
586 });
587 }
588}
589
James Feistbb43d022018-06-12 15:44:33 -0700590template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800591void addProperty(const std::string& propertyName, const PropertyType& value,
592 sdbusplus::asio::dbus_interface* iface,
593 nlohmann::json& systemConfiguration,
594 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700595 sdbusplus::asio::PropertyPermission permission)
596{
597 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
598 {
599 iface->register_property(propertyName, value);
600 return;
601 }
James Feist68500ff2018-08-08 15:40:42 -0700602 iface->register_property(
603 propertyName, value,
604 [&systemConfiguration,
605 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800606 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700607 val = newVal;
608 if (!setJsonFromPointer(jsonPointerString, val,
609 systemConfiguration))
610 {
611 std::cerr << "error setting json field\n";
612 return -1;
613 }
James Feistc6248a52018-08-14 10:09:45 -0700614 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700615 {
616 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700617 return -1;
618 }
619 return 1;
620 });
621}
622
623void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800624 const std::string& jsonPointerPath,
625 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
626 sdbusplus::asio::object_server& objServer,
627 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700628{
629 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
630 iface->register_method(
631 "Delete", [&objServer, &systemConfiguration, interface,
632 jsonPointerPath{std::string(jsonPointerPath)}]() {
633 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
634 interface.lock();
635 if (!iface)
636 {
637 // this technically can't happen as the pointer is pointing to
638 // us
639 throw DBusInternalError();
640 }
641 nlohmann::json::json_pointer ptr(jsonPointerPath);
642 if (!objServer.remove_interface(iface))
643 {
644 std::cerr << "Can't delete interface " << jsonPointerPath
645 << "\n";
646 throw DBusInternalError();
647 }
648 systemConfiguration[ptr] = nullptr;
649
650 if (!writeJsonFiles(systemConfiguration))
651 {
652 std::cerr << "error setting json file\n";
653 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700654 }
James Feistbb43d022018-06-12 15:44:33 -0700655 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700656 });
James Feistbb43d022018-06-12 15:44:33 -0700657}
658
James Feist1b2e2242018-01-30 13:45:19 -0800659// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700660void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800661 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
662 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
663 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700664 sdbusplus::asio::PropertyPermission permission =
665 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800666{
James Feista465ccc2019-02-08 12:51:01 -0800667 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800668 {
James Feist8f2710a2018-05-09 17:18:55 -0700669 auto type = dictPair.value().type();
670 bool array = false;
671 if (dictPair.value().type() == nlohmann::json::value_t::array)
672 {
673 array = true;
674 if (!dictPair.value().size())
675 {
676 continue;
677 }
678 type = dictPair.value()[0].type();
679 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800680 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700681 {
682 if (arrayItem.type() != type)
683 {
684 isLegal = false;
685 break;
686 }
687 }
688 if (!isLegal)
689 {
690 std::cerr << "dbus format error" << dictPair.value() << "\n";
691 continue;
692 }
693 if (type == nlohmann::json::value_t::object)
694 {
695 continue; // handled elsewhere
696 }
697 }
James Feist97a63f12018-05-17 13:50:57 -0700698 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700699 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
700 {
701 // all setable numbers are doubles as it is difficult to always
702 // create a configuration file with all whole numbers as decimals
703 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700704 if (array)
705 {
706 if (dictPair.value()[0].is_number())
707 {
708 type = nlohmann::json::value_t::number_float;
709 }
710 }
711 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700712 {
713 type = nlohmann::json::value_t::number_float;
714 }
715 }
716
James Feist8f2710a2018-05-09 17:18:55 -0700717 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800718 {
James Feist9eb0b582018-04-27 12:15:46 -0700719 case (nlohmann::json::value_t::boolean):
720 {
James Feist8f2710a2018-05-09 17:18:55 -0700721 if (array)
722 {
723 // todo: array of bool isn't detected correctly by
724 // sdbusplus, change it to numbers
725 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700726 iface.get(), permission,
727 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700728 }
James Feistbb43d022018-06-12 15:44:33 -0700729
James Feist97a63f12018-05-17 13:50:57 -0700730 else
731 {
James Feistbb43d022018-06-12 15:44:33 -0700732 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700733 iface.get(), systemConfiguration, key,
734 permission);
James Feist97a63f12018-05-17 13:50:57 -0700735 }
James Feist9eb0b582018-04-27 12:15:46 -0700736 break;
737 }
738 case (nlohmann::json::value_t::number_integer):
739 {
James Feist8f2710a2018-05-09 17:18:55 -0700740 if (array)
741 {
742 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700743 iface.get(), permission,
744 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700745 }
746 else
747 {
James Feistbb43d022018-06-12 15:44:33 -0700748 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700749 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700750 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700751 }
James Feist9eb0b582018-04-27 12:15:46 -0700752 break;
753 }
754 case (nlohmann::json::value_t::number_unsigned):
755 {
James Feist8f2710a2018-05-09 17:18:55 -0700756 if (array)
757 {
758 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700759 iface.get(), permission,
760 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700761 }
762 else
763 {
James Feistbb43d022018-06-12 15:44:33 -0700764 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700765 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700766 systemConfiguration, key,
767 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700768 }
James Feist9eb0b582018-04-27 12:15:46 -0700769 break;
770 }
771 case (nlohmann::json::value_t::number_float):
772 {
James Feist8f2710a2018-05-09 17:18:55 -0700773 if (array)
774 {
775 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700776 iface.get(), permission,
777 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700778 }
James Feistbb43d022018-06-12 15:44:33 -0700779
James Feist97a63f12018-05-17 13:50:57 -0700780 else
781 {
James Feistbb43d022018-06-12 15:44:33 -0700782 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700783 iface.get(), systemConfiguration, key,
784 permission);
James Feist97a63f12018-05-17 13:50:57 -0700785 }
James Feist9eb0b582018-04-27 12:15:46 -0700786 break;
787 }
788 case (nlohmann::json::value_t::string):
789 {
James Feist8f2710a2018-05-09 17:18:55 -0700790 if (array)
791 {
James Feistebcc26b2019-03-22 12:30:43 -0700792 addArrayToDbus<std::string>(
793 dictPair.key(), dictPair.value(), iface.get(),
794 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700795 }
796 else
797 {
James Feistc6248a52018-08-14 10:09:45 -0700798 addProperty(
799 dictPair.key(), dictPair.value().get<std::string>(),
800 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700801 }
James Feist9eb0b582018-04-27 12:15:46 -0700802 break;
803 }
James Feist0eb40352019-04-09 14:44:04 -0700804 default:
805 {
806 std::cerr << "Unexpected json type in system configuration \n";
807 break;
808 }
James Feist1b2e2242018-01-30 13:45:19 -0800809 }
810 }
James Feistc6248a52018-08-14 10:09:45 -0700811 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
812 {
813 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
814 systemConfiguration);
815 }
James Feist8f2710a2018-05-09 17:18:55 -0700816 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800817}
818
James Feista465ccc2019-02-08 12:51:01 -0800819sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700820{
821 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
822 interface) != settableInterfaces.end()
823 ? sdbusplus::asio::PropertyPermission::readWrite
824 : sdbusplus::asio::PropertyPermission::readOnly;
825}
826
James Feista465ccc2019-02-08 12:51:01 -0800827void createAddObjectMethod(const std::string& jsonPointerPath,
828 const std::string& path,
829 nlohmann::json& systemConfiguration,
830 sdbusplus::asio::object_server& objServer)
James Feist68500ff2018-08-08 15:40:42 -0700831{
832 auto iface = objServer.add_interface(path, "xyz.openbmc_project.AddObject");
833
834 iface->register_method(
835 "AddObject",
836 [&systemConfiguration, &objServer,
837 jsonPointerPath{std::string(jsonPointerPath)},
838 path{std::string(path)}](
James Feista465ccc2019-02-08 12:51:01 -0800839 const boost::container::flat_map<std::string, JsonVariantType>&
840 data) {
James Feist68500ff2018-08-08 15:40:42 -0700841 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800842 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700843 auto findExposes = base.find("Exposes");
844
845 if (findExposes == base.end())
846 {
847 throw std::invalid_argument("Entity must have children.");
848 }
849
850 // this will throw invalid-argument to sdbusplus if invalid json
851 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800852 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700853 {
James Feista465ccc2019-02-08 12:51:01 -0800854 nlohmann::json& newJson = newData[item.first];
855 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
856 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700857 }
858
859 auto findName = newData.find("Name");
860 auto findType = newData.find("Type");
861 if (findName == newData.end() || findType == newData.end())
862 {
863 throw std::invalid_argument("AddObject missing Name or Type");
864 }
James Feista465ccc2019-02-08 12:51:01 -0800865 const std::string* type = findType->get_ptr<const std::string*>();
866 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700867 if (type == nullptr || name == nullptr)
868 {
869 throw std::invalid_argument("Type and Name must be a string.");
870 }
871
872 size_t lastIndex = 0;
873 // we add in the "exposes"
874 for (; lastIndex < findExposes->size(); lastIndex++)
875 {
876 if (findExposes->at(lastIndex)["Name"] == *name &&
877 findExposes->at(lastIndex)["Type"] == *type)
878 {
879 throw std::invalid_argument(
880 "Field already in JSON, not adding");
881 }
882 lastIndex++;
883 }
884
885 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
886 *type + ".json");
887 // todo(james) we might want to also make a list of 'can add'
888 // interfaces but for now I think the assumption if there is a
889 // schema avaliable that it is allowed to update is fine
890 if (!schemaFile.good())
891 {
892 throw std::invalid_argument(
893 "No schema avaliable, cannot validate.");
894 }
895 nlohmann::json schema =
896 nlohmann::json::parse(schemaFile, nullptr, false);
897 if (schema.is_discarded())
898 {
899 std::cerr << "Schema not legal" << *type << ".json\n";
900 throw DBusInternalError();
901 }
902 if (!validateJson(schema, newData))
903 {
904 throw std::invalid_argument("Data does not match schema");
905 }
906
907 if (!writeJsonFiles(systemConfiguration))
908 {
909 std::cerr << "Error writing json files\n";
910 throw DBusInternalError();
911 }
912 std::string dbusName = *name;
913
914 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700915 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist68500ff2018-08-08 15:40:42 -0700916 auto iface = objServer.add_interface(
917 path + "/" + dbusName,
918 "xyz.openbmc_project.Configuration." + *type);
919 // permission is read-write, as since we just created it, must be
920 // runtime modifiable
921 populateInterfaceFromJson(
922 systemConfiguration,
James Feistc6248a52018-08-14 10:09:45 -0700923 jsonPointerPath + "/" + std::to_string(lastIndex), iface,
James Feist68500ff2018-08-08 15:40:42 -0700924 newData, objServer,
925 sdbusplus::asio::PropertyPermission::readWrite);
926 // todo(james) generate patch
927 findExposes->push_back(newData);
928 });
929 iface->initialize();
930}
931
James Feista465ccc2019-02-08 12:51:01 -0800932void postToDbus(const nlohmann::json& newConfiguration,
933 nlohmann::json& systemConfiguration,
934 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800935
James Feist1b2e2242018-01-30 13:45:19 -0800936{
James Feist97a63f12018-05-17 13:50:57 -0700937 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800938 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800939 {
940 std::string boardKey = boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700941 std::string jsonPointerPath = "/" + boardKey;
942 // loop through newConfiguration, but use values from system
943 // configuration to be able to modify via dbus later
944 auto boardValues = systemConfiguration[boardKey];
James Feistd63d18a2018-07-19 15:23:45 -0700945 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800946 std::string boardType;
947 if (findBoardType != boardValues.end() &&
948 findBoardType->type() == nlohmann::json::value_t::string)
949 {
950 boardType = findBoardType->get<std::string>();
951 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700952 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800953 }
954 else
955 {
956 std::cerr << "Unable to find type for " << boardKey
957 << " reverting to Chassis.\n";
958 boardType = "Chassis";
959 }
James Feist11be6672018-04-06 14:05:32 -0700960 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800961
962 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700963 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700964 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
965 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800966
James Feist8f2710a2018-05-09 17:18:55 -0700967 auto inventoryIface = objServer.add_interface(
968 boardName, "xyz.openbmc_project.Inventory.Item");
James Feist68500ff2018-08-08 15:40:42 -0700969
James Feist8f2710a2018-05-09 17:18:55 -0700970 auto boardIface = objServer.add_interface(
971 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700972
James Feist68500ff2018-08-08 15:40:42 -0700973 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
974 objServer);
975
James Feist97a63f12018-05-17 13:50:57 -0700976 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700977 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700978 jsonPointerPath += "/";
979 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800980 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700981 {
982 if (boardField.value().type() == nlohmann::json::value_t::object)
983 {
James Feist8f2710a2018-05-09 17:18:55 -0700984 auto iface =
985 objServer.add_interface(boardName, boardField.key());
James Feistc6248a52018-08-14 10:09:45 -0700986 populateInterfaceFromJson(systemConfiguration,
987 jsonPointerPath + boardField.key(),
988 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700989 }
990 }
James Feist97a63f12018-05-17 13:50:57 -0700991
James Feist1e3e6982018-08-03 16:09:28 -0700992 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800993 if (exposes == boardValues.end())
994 {
995 continue;
996 }
James Feist97a63f12018-05-17 13:50:57 -0700997 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700998 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700999
1000 // store the board level pointer so we can modify it on the way down
1001 std::string jsonPointerPathBoard = jsonPointerPath;
1002 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001003 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001004 {
James Feist97a63f12018-05-17 13:50:57 -07001005 exposesIndex++;
1006 jsonPointerPath = jsonPointerPathBoard;
1007 jsonPointerPath += std::to_string(exposesIndex);
1008
James Feistd63d18a2018-07-19 15:23:45 -07001009 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001010 if (findName == item.end())
1011 {
1012 std::cerr << "cannot find name in field " << item << "\n";
1013 continue;
1014 }
James Feist1e3e6982018-08-03 16:09:28 -07001015 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001016 // if status is not found it is assumed to be status = 'okay'
1017 if (findStatus != item.end())
1018 {
1019 if (*findStatus == "disabled")
1020 {
1021 continue;
1022 }
1023 }
James Feistd63d18a2018-07-19 15:23:45 -07001024 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001025 std::string itemType;
1026 if (findType != item.end())
1027 {
1028 itemType = findType->get<std::string>();
1029 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001030 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1031 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001032 }
1033 else
1034 {
1035 itemType = "unknown";
1036 }
1037 std::string itemName = findName->get<std::string>();
1038 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001039 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001040
James Feist8f2710a2018-05-09 17:18:55 -07001041 auto itemIface = objServer.add_interface(
1042 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001043 "xyz.openbmc_project.Configuration." + itemType);
1044
James Feist97a63f12018-05-17 13:50:57 -07001045 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001046 itemIface, item, objServer,
1047 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001048
James Feista465ccc2019-02-08 12:51:01 -08001049 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001050 {
James Feist97a63f12018-05-17 13:50:57 -07001051 jsonPointerPath = jsonPointerPathBoard +
1052 std::to_string(exposesIndex) + "/" +
1053 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001054 if (objectPair.value().type() ==
1055 nlohmann::json::value_t::object)
1056 {
James Feist8f2710a2018-05-09 17:18:55 -07001057 auto objectIface = objServer.add_interface(
1058 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001059 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -07001060 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -07001061
1062 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001063 systemConfiguration, jsonPointerPath, objectIface,
1064 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001065 }
1066 else if (objectPair.value().type() ==
1067 nlohmann::json::value_t::array)
1068 {
1069 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001070 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001071 {
James Feist8f2710a2018-05-09 17:18:55 -07001072 continue;
1073 }
1074 bool isLegal = true;
1075 auto type = objectPair.value()[0].type();
1076 if (type != nlohmann::json::value_t::object)
1077 {
1078 continue;
1079 }
1080
1081 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001082 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001083 {
1084 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001085 {
James Feist8f2710a2018-05-09 17:18:55 -07001086 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001087 break;
1088 }
James Feist8f2710a2018-05-09 17:18:55 -07001089 }
1090 if (!isLegal)
1091 {
1092 std::cerr << "dbus format error" << objectPair.value()
1093 << "\n";
1094 break;
1095 }
1096
James Feista465ccc2019-02-08 12:51:01 -08001097 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001098 {
James Feist97a63f12018-05-17 13:50:57 -07001099
James Feist8f2710a2018-05-09 17:18:55 -07001100 auto objectIface = objServer.add_interface(
1101 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001102 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -07001103 "." + objectPair.key() + std::to_string(index));
James Feistc6248a52018-08-14 10:09:45 -07001104 populateInterfaceFromJson(
1105 systemConfiguration,
1106 jsonPointerPath + "/" + std::to_string(index),
1107 objectIface, arrayItem, objServer,
1108 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001109 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001110 }
1111 }
1112 }
1113 }
1114 }
1115}
1116
1117// finds the template character (currently set to $) and replaces the value with
1118// the field found in a dbus object i.e. $ADDRESS would get populated with the
1119// ADDRESS field from a object on dbus
1120void templateCharReplace(
James Feista465ccc2019-02-08 12:51:01 -08001121 nlohmann::json::iterator& keyPair,
1122 const boost::container::flat_map<std::string, BasicVariantType>&
1123 foundDevice,
1124 size_t& foundDeviceIdx)
James Feist1b2e2242018-01-30 13:45:19 -08001125{
James Feist11be6672018-04-06 14:05:32 -07001126 if (keyPair.value().type() == nlohmann::json::value_t::object)
1127 {
1128 for (auto nextLayer = keyPair.value().begin();
1129 nextLayer != keyPair.value().end(); nextLayer++)
1130 {
1131 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
1132 }
1133 return;
1134 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001135
1136 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
1137 if (strPtr == nullptr)
James Feist1b2e2242018-01-30 13:45:19 -08001138 {
1139 return;
1140 }
1141
Ed Tanous12bc7932019-02-26 14:36:20 -08001142 boost::replace_all(*strPtr, "$index", std::to_string(foundDeviceIdx));
1143
Ed Tanous12bc7932019-02-26 14:36:20 -08001144 for (auto& foundDevicePair : foundDevice)
James Feist1b2e2242018-01-30 13:45:19 -08001145 {
Ed Tanous12bc7932019-02-26 14:36:20 -08001146 std::string templateName = "$" + foundDevicePair.first;
1147 if (boost::iequals(*strPtr, templateName))
James Feist1b2e2242018-01-30 13:45:19 -08001148 {
Ed Tanous12bc7932019-02-26 14:36:20 -08001149 std::visit([&](auto&& val) { keyPair.value() = val; },
1150 foundDevicePair.second);
1151 // We probably just invalidated the pointer above, so set it to null
1152 strPtr = nullptr;
1153 break;
James Feist1b2e2242018-01-30 13:45:19 -08001154 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001155
1156 std::string probeValue =
1157 std::visit(VariantToStringVisitor(), foundDevicePair.second);
1158 boost::replace_all(*strPtr, templateName, probeValue);
1159 }
1160
1161 strPtr = keyPair.value().get_ptr<std::string*>();
1162 if (strPtr == nullptr)
1163 {
1164 return;
James Feist1b2e2242018-01-30 13:45:19 -08001165 }
James Feistc6090822019-01-04 16:02:48 -08001166
1167 // convert hex numbers to ints
Ed Tanous12bc7932019-02-26 14:36:20 -08001168 if (boost::starts_with(*strPtr, "0x"))
James Feist28dc2da2018-10-15 14:47:42 -07001169 {
1170 try
1171 {
James Feistc6090822019-01-04 16:02:48 -08001172 size_t pos = 0;
Ed Tanous12bc7932019-02-26 14:36:20 -08001173 int64_t temp = std::stoul(*strPtr, &pos, 0);
1174 if (pos == strPtr->size())
James Feistc6090822019-01-04 16:02:48 -08001175 {
1176 keyPair.value() = static_cast<uint64_t>(temp);
1177 }
James Feist28dc2da2018-10-15 14:47:42 -07001178 }
1179 catch (std::invalid_argument)
1180 {
1181 }
James Feistc6090822019-01-04 16:02:48 -08001182 catch (std::out_of_range)
1183 {
1184 }
James Feist28dc2da2018-10-15 14:47:42 -07001185 }
James Feist1b2e2242018-01-30 13:45:19 -08001186}
1187
James Feist8f2710a2018-05-09 17:18:55 -07001188// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001189bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001190{
1191 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001192 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001193 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1194 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001195 {
1196 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001197 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001198 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001199 }
James Feistb4383f42018-08-06 16:54:10 -07001200
1201 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1202 globalSchema);
1203 if (!schemaStream.good())
1204 {
1205 std::cerr
1206 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1207 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001208 return false;
James Feistb4383f42018-08-06 16:54:10 -07001209 }
1210 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1211 if (schema.is_discarded())
1212 {
1213 std::cerr
1214 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1215 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001216 return false;
James Feistb4383f42018-08-06 16:54:10 -07001217 }
1218
James Feista465ccc2019-02-08 12:51:01 -08001219 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001220 {
1221 std::ifstream jsonStream(jsonPath.c_str());
1222 if (!jsonStream.good())
1223 {
1224 std::cerr << "unable to open " << jsonPath.string() << "\n";
1225 continue;
1226 }
1227 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1228 if (data.is_discarded())
1229 {
1230 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1231 continue;
1232 }
James Feist8da99192019-01-24 08:20:16 -08001233 /*
1234 * todo(james): reenable this once less things are in flight
1235 *
James Feistb4383f42018-08-06 16:54:10 -07001236 if (!validateJson(schema, data))
1237 {
1238 std::cerr << "Error validating " << jsonPath.string() << "\n";
1239 continue;
1240 }
James Feist8da99192019-01-24 08:20:16 -08001241 */
James Feistb4383f42018-08-06 16:54:10 -07001242
James Feist3cb5fec2018-01-23 14:41:51 -08001243 if (data.type() == nlohmann::json::value_t::array)
1244 {
James Feista465ccc2019-02-08 12:51:01 -08001245 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001246 {
1247 configurations.emplace_back(d);
1248 }
1249 }
1250 else
1251 {
1252 configurations.emplace_back(data);
1253 }
1254 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001255 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001256}
James Feist3cb5fec2018-01-23 14:41:51 -08001257
James Feist8f2710a2018-05-09 17:18:55 -07001258struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001259{
James Feist75fdeeb2018-02-20 14:26:16 -08001260
James Feista465ccc2019-02-08 12:51:01 -08001261 PerformScan(nlohmann::json& systemConfiguration,
1262 std::list<nlohmann::json>& configurations,
1263 std::function<void(void)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -07001264 _systemConfiguration(systemConfiguration),
1265 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001266 {
James Feist8f2710a2018-05-09 17:18:55 -07001267 }
1268 void run()
1269 {
1270 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001271 {
James Feist1e3e6982018-08-03 16:09:28 -07001272 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001273 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001274
James Feist1b2e2242018-01-30 13:45:19 -08001275 nlohmann::json probeCommand;
1276 // check for poorly formatted fields, probe must be an array
1277 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001278 {
1279 std::cerr << "configuration file missing probe:\n " << *it
1280 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001281 it = _configurations.erase(it);
1282 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001283 }
James Feist1b2e2242018-01-30 13:45:19 -08001284 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001285 {
1286 probeCommand = nlohmann::json::array();
1287 probeCommand.push_back(*findProbe);
1288 }
1289 else
1290 {
1291 probeCommand = *findProbe;
1292 }
James Feist1b2e2242018-01-30 13:45:19 -08001293
1294 if (findName == it->end())
1295 {
1296 std::cerr << "configuration file missing name:\n " << *it
1297 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001298 it = _configurations.erase(it);
1299 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001300 }
James Feist8f2710a2018-05-09 17:18:55 -07001301 std::string name = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001302
James Feist8f2710a2018-05-09 17:18:55 -07001303 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(), name) !=
1304 PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001305 {
James Feist8f2710a2018-05-09 17:18:55 -07001306 it = _configurations.erase(it);
1307 continue;
1308 }
James Feista465ccc2019-02-08 12:51:01 -08001309 nlohmann::json* record = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001310
James Feist8f2710a2018-05-09 17:18:55 -07001311 // store reference to this to children to makes sure we don't get
1312 // destroyed too early
1313 auto thisRef = shared_from_this();
1314 auto p = std::make_shared<PerformProbe>(
1315 probeCommand,
1316 [&, record, name,
1317 thisRef](std::vector<boost::container::flat_map<
James Feista465ccc2019-02-08 12:51:01 -08001318 std::string, BasicVariantType>>& foundDevices) {
James Feist8f2710a2018-05-09 17:18:55 -07001319 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001320
James Feist8f2710a2018-05-09 17:18:55 -07001321 PASSED_PROBES.push_back(name);
1322 size_t foundDeviceIdx = 0;
1323
James Feist0eb40352019-04-09 14:44:04 -07001324 // insert into configuration temporarily to be able to
James Feistbe5425f2018-06-08 10:30:55 -07001325 // reference ourselves
1326 _systemConfiguration[name] = *record;
1327
James Feista465ccc2019-02-08 12:51:01 -08001328 for (auto& foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001329 {
James Feist8f2710a2018-05-09 17:18:55 -07001330 for (auto keyPair = record->begin();
1331 keyPair != record->end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001332 {
James Feist1b2e2242018-01-30 13:45:19 -08001333 templateCharReplace(keyPair, foundDevice,
1334 foundDeviceIdx);
James Feist8f2710a2018-05-09 17:18:55 -07001335 }
James Feist1e3e6982018-08-03 16:09:28 -07001336 auto findExpose = record->find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001337 if (findExpose == record->end())
1338 {
1339 continue;
1340 }
James Feista465ccc2019-02-08 12:51:01 -08001341 for (auto& expose : *findExpose)
James Feist8f2710a2018-05-09 17:18:55 -07001342 {
1343 for (auto keyPair = expose.begin();
1344 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001345 {
James Feist1b2e2242018-01-30 13:45:19 -08001346
James Feist8f2710a2018-05-09 17:18:55 -07001347 // fill in template characters with devices
1348 // found
1349 templateCharReplace(keyPair, foundDevice,
1350 foundDeviceIdx);
1351 // special case bind
James Feist1e3e6982018-08-03 16:09:28 -07001352 if (boost::starts_with(keyPair.key(), "Bind"))
James Feist8f2710a2018-05-09 17:18:55 -07001353 {
1354 if (keyPair.value().type() !=
1355 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001356 {
James Feist8f2710a2018-05-09 17:18:55 -07001357 std::cerr << "bind_ value must be of "
1358 "type string "
1359 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001360 continue;
1361 }
James Feist8f2710a2018-05-09 17:18:55 -07001362 bool foundBind = false;
1363 std::string bind = keyPair.key().substr(
James Feist1e3e6982018-08-03 16:09:28 -07001364 sizeof("Bind") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001365
James Feista465ccc2019-02-08 12:51:01 -08001366 for (auto& configurationPair :
James Feist8f2710a2018-05-09 17:18:55 -07001367 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001368 {
James Feist1b2e2242018-01-30 13:45:19 -08001369
James Feist8f2710a2018-05-09 17:18:55 -07001370 auto configListFind =
1371 configurationPair.value().find(
James Feist1e3e6982018-08-03 16:09:28 -07001372 "Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001373
1374 if (configListFind ==
1375 configurationPair.value()
1376 .end() ||
1377 configListFind->type() !=
1378 nlohmann::json::value_t::array)
1379 {
1380 continue;
1381 }
James Feista465ccc2019-02-08 12:51:01 -08001382 for (auto& exposedObject :
James Feist8f2710a2018-05-09 17:18:55 -07001383 *configListFind)
1384 {
1385 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001386 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001387 if (boost::iequals(
1388 foundObjectName,
1389 keyPair.value()
1390 .get<std::string>()))
1391 {
James Feist1e3e6982018-08-03 16:09:28 -07001392 exposedObject["Status"] =
James Feist8f2710a2018-05-09 17:18:55 -07001393 "okay";
1394 expose[bind] = exposedObject;
1395
1396 foundBind = true;
1397 break;
1398 }
1399 }
1400 if (foundBind)
1401 {
James Feist3cb5fec2018-01-23 14:41:51 -08001402 break;
1403 }
1404 }
James Feist8f2710a2018-05-09 17:18:55 -07001405 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001406 {
James Feist8f2710a2018-05-09 17:18:55 -07001407 std::cerr << "configuration file "
1408 "dependency error, "
1409 "could not find bind "
1410 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001411 }
1412 }
1413 }
1414 }
1415 }
James Feistbe5425f2018-06-08 10:30:55 -07001416 // overwrite ourselves with cleaned up version
James Feist8f2710a2018-05-09 17:18:55 -07001417 _systemConfiguration[name] = *record;
1418 });
1419 p->run();
1420 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001421 }
1422 }
James Feist75fdeeb2018-02-20 14:26:16 -08001423
James Feist8f2710a2018-05-09 17:18:55 -07001424 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001425 {
James Feist8f2710a2018-05-09 17:18:55 -07001426 if (_passed)
1427 {
1428 auto nextScan = std::make_shared<PerformScan>(
1429 _systemConfiguration, _configurations, std::move(_callback));
1430 nextScan->run();
1431 }
1432 else
1433 {
1434 _callback();
1435 }
1436 }
James Feista465ccc2019-02-08 12:51:01 -08001437 nlohmann::json& _systemConfiguration;
James Feist8f2710a2018-05-09 17:18:55 -07001438 std::list<nlohmann::json> _configurations;
1439 std::function<void(void)> _callback;
1440 std::vector<std::shared_ptr<PerformProbe>> _probes;
1441 bool _passed = false;
1442};
James Feistc95cb142018-02-26 10:41:42 -08001443
James Feist8f2710a2018-05-09 17:18:55 -07001444// main properties changed entry
1445void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001446 boost::asio::io_service& io,
1447 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1448 nlohmann::json& systemConfiguration,
1449 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001450{
1451 static boost::asio::deadline_timer timer(io);
1452 timer.expires_from_now(boost::posix_time::seconds(1));
1453
1454 // setup an async wait as we normally get flooded with new requests
James Feista465ccc2019-02-08 12:51:01 -08001455 timer.async_wait([&](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001456 if (ec == boost::asio::error::operation_aborted)
1457 {
1458 // we were cancelled
1459 return;
1460 }
1461 else if (ec)
1462 {
1463 std::cerr << "async wait error " << ec << "\n";
1464 return;
1465 }
1466
1467 nlohmann::json oldConfiguration = systemConfiguration;
1468 DBUS_PROBE_OBJECTS.clear();
1469
1470 std::list<nlohmann::json> configurations;
1471 if (!findJsonFiles(configurations))
1472 {
1473 std::cerr << "cannot find json files\n";
1474 return;
1475 }
1476
1477 auto perfScan = std::make_shared<PerformScan>(
1478 systemConfiguration, configurations, [&, oldConfiguration]() {
1479 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001480 for (auto it = newConfiguration.begin();
1481 it != newConfiguration.end();)
1482 {
1483 auto findKey = oldConfiguration.find(it.key());
1484 if (findKey != oldConfiguration.end())
1485 {
1486 it = newConfiguration.erase(it);
1487 }
1488 else
1489 {
1490 it++;
1491 }
1492 }
James Feist8f2710a2018-05-09 17:18:55 -07001493 registerCallbacks(io, dbusMatches, systemConfiguration,
1494 objServer);
1495 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001496 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001497
James Feistbb43d022018-06-12 15:44:33 -07001498 io.post([&]() {
1499 if (!writeJsonFiles(systemConfiguration))
1500 {
1501 std::cerr << "Error writing json files\n";
1502 }
1503 });
James Feist8f2710a2018-05-09 17:18:55 -07001504 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001505 postToDbus(newConfiguration, systemConfiguration,
1506 objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001507 });
1508 });
1509 });
1510 perfScan->run();
1511 });
James Feist75fdeeb2018-02-20 14:26:16 -08001512}
1513
James Feista465ccc2019-02-08 12:51:01 -08001514void registerCallbacks(boost::asio::io_service& io,
1515 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1516 nlohmann::json& systemConfiguration,
1517 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001518{
1519 static boost::container::flat_set<std::string> watchedObjects;
1520
James Feista465ccc2019-02-08 12:51:01 -08001521 for (const auto& objectMap : DBUS_PROBE_OBJECTS)
James Feist75fdeeb2018-02-20 14:26:16 -08001522 {
1523 auto findObject = watchedObjects.find(objectMap.first);
1524 if (findObject != watchedObjects.end())
1525 {
1526 continue;
1527 }
James Feist8f2710a2018-05-09 17:18:55 -07001528 std::function<void(sdbusplus::message::message & message)>
1529 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001530
James Feista465ccc2019-02-08 12:51:01 -08001531 [&](sdbusplus::message::message&) {
James Feist8f2710a2018-05-09 17:18:55 -07001532 propertiesChangedCallback(io, dbusMatches,
1533 systemConfiguration, objServer);
1534 };
1535
1536 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001537 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001538 "type='signal',member='PropertiesChanged',arg0='" +
1539 objectMap.first + "'",
1540 eventHandler);
1541 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001542 }
1543}
1544
James Feista465ccc2019-02-08 12:51:01 -08001545int main(int argc, char** argv)
James Feist75fdeeb2018-02-20 14:26:16 -08001546{
1547 // setup connection to dbus
1548 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001549 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001550 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001551
James Feist8f2710a2018-05-09 17:18:55 -07001552 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001553
James Feist8f2710a2018-05-09 17:18:55 -07001554 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1555 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1556 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001557
James Feist8f2710a2018-05-09 17:18:55 -07001558 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1559 objServer.add_interface("/xyz/openbmc_project/inventory",
1560 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001561
1562 // to keep reference to the match / filter objects so they don't get
1563 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001564 std::vector<sdbusplus::bus::match::match> dbusMatches;
1565
1566 nlohmann::json systemConfiguration = nlohmann::json::object();
1567
1568 inventoryIface->register_method(
James Feista465ccc2019-02-08 12:51:01 -08001569 "Notify",
1570 [](const boost::container::flat_map<
1571 std::string,
1572 boost::container::flat_map<std::string, BasicVariantType>>&
1573 object) { return; });
James Feist8f2710a2018-05-09 17:18:55 -07001574 inventoryIface->initialize();
1575
1576 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001577#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001578 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001579#endif
James Feist8f2710a2018-05-09 17:18:55 -07001580 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1581 objServer);
1582 });
James Feist4131aea2018-03-09 09:47:30 -08001583
James Feistfd1264a2018-05-03 12:10:00 -07001584 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001585 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1586 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001587 });
James Feist8f2710a2018-05-09 17:18:55 -07001588 entityIface->initialize();
1589
James Feist1b2e2242018-01-30 13:45:19 -08001590 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001591
1592 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001593}