blob: ab658b2b779e9172fc59adfd07fc7024df675541 [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
17#include <Utils.hpp>
James Feistc95cb142018-02-26 10:41:42 -080018#include <Overlay.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080019#include <nlohmann/json.hpp>
20#include <fstream>
21#include <regex>
James Feist8f2710a2018-05-09 17:18:55 -070022#include <iostream>
23#include <sdbusplus/asio/connection.hpp>
24#include <sdbusplus/asio/object_server.hpp>
James Feist11be6672018-04-06 14:05:32 -070025#include <boost/algorithm/string/case_conv.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080026#include <boost/algorithm/string/predicate.hpp>
27#include <boost/algorithm/string/replace.hpp>
28#include <boost/variant/apply_visitor.hpp>
29#include <boost/lexical_cast.hpp>
30#include <boost/container/flat_map.hpp>
31#include <boost/container/flat_set.hpp>
32#include <VariantVisitors.hpp>
James Feist7b7e4e82018-01-24 14:56:00 -080033#include <experimental/filesystem>
James Feist3cb5fec2018-01-23 14:41:51 -080034
35constexpr const char *OUTPUT_DIR = "/var/configuration/";
36constexpr const char *CONFIGURATION_DIR = "/usr/share/configurations";
James Feist1e3e6982018-08-03 16:09:28 -070037constexpr const char *schemaFile = "schema.json";
James Feist3cb5fec2018-01-23 14:41:51 -080038constexpr const char *TEMPLATE_CHAR = "$";
James Feistc95cb142018-02-26 10:41:42 -080039constexpr const size_t PROPERTIES_CHANGED_UNTIL_FLUSH_COUNT = 20;
James Feist8f2710a2018-05-09 17:18:55 -070040constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist4131aea2018-03-09 09:47:30 -080041constexpr const size_t SLEEP_AFTER_PROPERTIES_CHANGE_SECONDS = 5;
James Feist3cb5fec2018-01-23 14:41:51 -080042
43namespace fs = std::experimental::filesystem;
44struct cmp_str
45{
46 bool operator()(const char *a, const char *b) const
47 {
48 return std::strcmp(a, b) < 0;
49 }
50};
51
James Feist8f2710a2018-05-09 17:18:55 -070052struct PerformProbe;
53
James Feist3cb5fec2018-01-23 14:41:51 -080054// underscore T for collison with dbus c api
55enum class probe_type_codes
56{
57 FALSE_T,
58 TRUE_T,
59 AND,
60 OR,
James Feist6bd2a022018-03-13 12:30:58 -070061 FOUND,
62 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080063};
64const static boost::container::flat_map<const char *, probe_type_codes, cmp_str>
65 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
66 {"TRUE", probe_type_codes::TRUE_T},
67 {"AND", probe_type_codes::AND},
68 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070069 {"FOUND", probe_type_codes::FOUND},
70 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080071
James Feist97a63f12018-05-17 13:50:57 -070072static constexpr std::array<const char *, 1> SETTABLE_INTERFACES = {
James Feist1e3e6982018-08-03 16:09:28 -070073 "Thresholds"};
James Feist97a63f12018-05-17 13:50:57 -070074
James Feist8f2710a2018-05-09 17:18:55 -070075using BasicVariantType =
76 sdbusplus::message::variant<std::string, int64_t, uint64_t, double, int32_t,
77 uint32_t, int16_t, uint16_t, uint8_t, bool>;
78
James Feist3cb5fec2018-01-23 14:41:51 -080079using GetSubTreeType = std::vector<
80 std::pair<std::string,
81 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
82
83using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070084 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080085 boost::container::flat_map<
86 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070087 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080088
89boost::container::flat_map<
90 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070091 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -080092 DBUS_PROBE_OBJECTS;
93std::vector<std::string> PASSED_PROBES;
94
95// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -070096std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist3cb5fec2018-01-23 14:41:51 -080097
James Feista6750242018-07-16 14:12:27 -070098const std::regex ILLEGAL_DBUS_REGEX("[^A-Za-z0-9_.]");
James Feist1b2e2242018-01-30 13:45:19 -080099
James Feist8f2710a2018-05-09 17:18:55 -0700100void registerCallbacks(boost::asio::io_service &io,
101 std::vector<sdbusplus::bus::match::match> &dbusMatches,
102 nlohmann::json &systemConfiguration,
103 sdbusplus::asio::object_server &objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800104
James Feist3cb5fec2018-01-23 14:41:51 -0800105// calls the mapper to find all exposed objects of an interface type
106// and creates a vector<flat_map> that contains all the key value pairs
107// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700108void findDbusObjects(std::shared_ptr<PerformProbe> probe,
109 std::shared_ptr<sdbusplus::asio::connection> connection,
110 std::string &interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800111{
James Feist8f2710a2018-05-09 17:18:55 -0700112
113 // store reference to pending callbacks so we don't overwhelm services
114 static boost::container::flat_map<
115 std::string, std::vector<std::shared_ptr<PerformProbe>>>
116 pendingProbes;
117
118 if (DBUS_PROBE_OBJECTS[interface].size())
119 {
120 return;
121 }
122
123 // add shared_ptr to vector of Probes waiting for callback from a specific
124 // interface to keep alive while waiting for response
125 std::array<const char *, 1> objects = {interface.c_str()};
126 std::vector<std::shared_ptr<PerformProbe>> &pending =
127 pendingProbes[interface];
128 auto iter = pending.emplace(pending.end(), probe);
129 // only allow first call to run to not overwhelm processes
130 if (iter != pending.begin())
131 {
132 return;
133 }
134
James Feist3cb5fec2018-01-23 14:41:51 -0800135 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700136 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700137 [connection, interface, probe](boost::system::error_code &ec,
138 const GetSubTreeType &interfaceSubtree) {
139 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700140 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700141 {
James Feist0de40152018-07-25 11:56:12 -0700142 pendingProbes[interface].clear();
143 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700144 {
James Feist0de40152018-07-25 11:56:12 -0700145 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700146 }
James Feist0de40152018-07-25 11:56:12 -0700147 std::cerr << "Error communicating to mapper.\n";
148
149 // if we can't communicate to the mapper something is very wrong
150 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700151 }
James Feist8f2710a2018-05-09 17:18:55 -0700152 else
James Feist3cb5fec2018-01-23 14:41:51 -0800153 {
James Feist8f2710a2018-05-09 17:18:55 -0700154 for (auto &object : interfaceSubtree)
155 {
156 for (auto &connPair : object.second)
157 {
158 auto insertPair =
159 interfaceConnections.insert(connPair.first);
160 }
161 }
James Feist3cb5fec2018-01-23 14:41:51 -0800162 }
James Feist8f2710a2018-05-09 17:18:55 -0700163 // get managed objects for all interfaces
164 for (const auto &conn : interfaceConnections)
165 {
166 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700167 [conn,
James Feist8f2710a2018-05-09 17:18:55 -0700168 interface](boost::system::error_code &ec,
169 const ManagedObjectType &managedInterface) {
170 if (ec)
171 {
172 std::cerr
173 << "error getting managed object for device "
174 << conn << "\n";
175 pendingProbes[interface].clear();
176 return;
177 }
178 for (auto &interfaceManagedObj : managedInterface)
179 {
180 auto ifaceObjFind =
181 interfaceManagedObj.second.find(interface);
182 if (ifaceObjFind !=
183 interfaceManagedObj.second.end())
184 {
185 std::vector<boost::container::flat_map<
186 std::string, BasicVariantType>>
187 &dbusObject = DBUS_PROBE_OBJECTS[interface];
188 dbusObject.emplace_back(ifaceObjFind->second);
189 }
190 }
191 pendingProbes[interface].clear();
192 },
193 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
194 "GetManagedObjects");
195 }
196 },
197 "xyz.openbmc_project.ObjectMapper",
198 "/xyz/openbmc_project/object_mapper",
199 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "", MAX_MAPPER_DEPTH,
200 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800201}
James Feist8f2710a2018-05-09 17:18:55 -0700202// probes dbus interface dictionary for a key with a value that matches a regex
James Feist3cb5fec2018-01-23 14:41:51 -0800203bool probeDbus(
204 const std::string &interface,
205 const std::map<std::string, nlohmann::json> &matches,
James Feist8f2710a2018-05-09 17:18:55 -0700206 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
James Feist3cb5fec2018-01-23 14:41:51 -0800207 &devices,
208 bool &foundProbe)
209{
James Feist8f2710a2018-05-09 17:18:55 -0700210 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
211 &dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800212 if (dbusObject.empty())
213 {
James Feist8f2710a2018-05-09 17:18:55 -0700214 foundProbe = false;
215 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800216 }
217 foundProbe = true;
218
219 bool foundMatch = false;
220 for (auto &device : dbusObject)
221 {
222 bool deviceMatches = true;
223 for (auto &match : matches)
224 {
225 auto deviceValue = device.find(match.first);
226 if (deviceValue != device.end())
227 {
228 switch (match.second.type())
229 {
James Feist9eb0b582018-04-27 12:15:46 -0700230 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800231 {
James Feist9eb0b582018-04-27 12:15:46 -0700232 std::regex search(match.second.get<std::string>());
233 std::smatch match;
234
235 // convert value to string respresentation
James Feist8f2710a2018-05-09 17:18:55 -0700236 std::string probeValue = mapbox::util::apply_visitor(
237 VariantToStringVisitor(), deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700238 if (!std::regex_search(probeValue, match, search))
239 {
240 deviceMatches = false;
241 break;
242 }
James Feist3cb5fec2018-01-23 14:41:51 -0800243 break;
244 }
James Feist9eb0b582018-04-27 12:15:46 -0700245 case nlohmann::json::value_t::boolean:
246 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800247 {
James Feist8f2710a2018-05-09 17:18:55 -0700248 unsigned int probeValue = mapbox::util::apply_visitor(
James Feist9eb0b582018-04-27 12:15:46 -0700249 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800250
James Feist9eb0b582018-04-27 12:15:46 -0700251 if (probeValue != match.second.get<unsigned int>())
252 {
253 deviceMatches = false;
254 }
255 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800256 }
James Feist9eb0b582018-04-27 12:15:46 -0700257 case nlohmann::json::value_t::number_integer:
258 {
James Feist8f2710a2018-05-09 17:18:55 -0700259 int probeValue = mapbox::util::apply_visitor(
James Feist9eb0b582018-04-27 12:15:46 -0700260 VariantToIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800261
James Feist9eb0b582018-04-27 12:15:46 -0700262 if (probeValue != match.second.get<int>())
263 {
264 deviceMatches = false;
265 }
266 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800267 }
James Feist9eb0b582018-04-27 12:15:46 -0700268 case nlohmann::json::value_t::number_float:
269 {
James Feist8f2710a2018-05-09 17:18:55 -0700270 float probeValue = mapbox::util::apply_visitor(
James Feist9eb0b582018-04-27 12:15:46 -0700271 VariantToFloatVisitor(), deviceValue->second);
272
273 if (probeValue != match.second.get<float>())
274 {
275 deviceMatches = false;
276 }
277 break;
278 }
James Feist3cb5fec2018-01-23 14:41:51 -0800279 }
280 }
281 else
282 {
283 deviceMatches = false;
284 break;
285 }
286 }
287 if (deviceMatches)
288 {
289 devices.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700290 boost::container::flat_map<std::string, BasicVariantType>(
James Feist3cb5fec2018-01-23 14:41:51 -0800291 device));
292 foundMatch = true;
293 deviceMatches = false; // for next iteration
294 }
295 }
296 return foundMatch;
297}
298
299// default probe entry point, iterates a list looking for specific types to
300// call specific probe functions
301bool probe(
James Feist8f2710a2018-05-09 17:18:55 -0700302 const std::vector<std::string> &probeCommand,
303 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
James Feist3cb5fec2018-01-23 14:41:51 -0800304 &foundDevs)
305{
306 const static std::regex command(R"(\((.*)\))");
307 std::smatch match;
308 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700309 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800310 bool cur = true;
311 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
312
313 for (auto &probe : probeCommand)
314 {
315 bool foundProbe = false;
316 boost::container::flat_map<const char *, probe_type_codes,
317 cmp_str>::const_iterator probeType;
318
319 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
320 probeType++)
321 {
322 if (probe.find(probeType->first) != std::string::npos)
323 {
324 foundProbe = true;
325 break;
326 }
327 }
328 if (foundProbe)
329 {
330 switch (probeType->second)
331 {
James Feist9eb0b582018-04-27 12:15:46 -0700332 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800333 {
James Feist8f2710a2018-05-09 17:18:55 -0700334 return false;
James Feist9eb0b582018-04-27 12:15:46 -0700335 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800336 }
James Feist9eb0b582018-04-27 12:15:46 -0700337 case probe_type_codes::TRUE_T:
338 {
James Feist8f2710a2018-05-09 17:18:55 -0700339 return true;
James Feist9eb0b582018-04-27 12:15:46 -0700340 break;
341 }
342 case probe_type_codes::MATCH_ONE:
343 {
344 // set current value to last, this probe type shouldn't
345 // affect the outcome
346 cur = ret;
347 matchOne = true;
348 break;
349 }
350 /*case probe_type_codes::AND:
351 break;
352 case probe_type_codes::OR:
353 break;
354 // these are no-ops until the last command switch
355 */
356 case probe_type_codes::FOUND:
357 {
358 if (!std::regex_search(probe, match, command))
359 {
360 std::cerr << "found probe sytax error " << probe
361 << "\n";
362 return false;
363 }
364 std::string commandStr = *(match.begin() + 1);
365 boost::replace_all(commandStr, "'", "");
366 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
367 commandStr) != PASSED_PROBES.end());
368 break;
369 }
James Feist3cb5fec2018-01-23 14:41:51 -0800370 }
371 }
372 // look on dbus for object
373 else
374 {
375 if (!std::regex_search(probe, match, command))
376 {
377 std::cerr << "dbus probe sytax error " << probe << "\n";
378 return false;
379 }
380 std::string commandStr = *(match.begin() + 1);
381 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700382 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800383 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800384 auto json = nlohmann::json::parse(commandStr, nullptr, false);
385 if (json.is_discarded())
386 {
387 std::cerr << "dbus command sytax error " << commandStr << "\n";
388 return false;
389 }
390 // we can match any (string, variant) property. (string, string)
391 // does a regex
392 std::map<std::string, nlohmann::json> dbusProbeMap =
393 json.get<std::map<std::string, nlohmann::json>>();
394 auto findStart = probe.find("(");
395 if (findStart == std::string::npos)
396 {
397 return false;
398 }
399 std::string probeInterface = probe.substr(0, findStart);
400 cur =
401 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
402 }
403
404 // some functions like AND and OR only take affect after the
405 // fact
406 switch (lastCommand)
407 {
James Feist9eb0b582018-04-27 12:15:46 -0700408 case probe_type_codes::AND:
409 ret = cur && ret;
410 break;
411 case probe_type_codes::OR:
412 ret = cur || ret;
413 break;
414 default:
415 ret = cur;
416 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800417 }
418 lastCommand = probeType != PROBE_TYPES.end()
419 ? probeType->second
420 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800421 }
422
423 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800424 if (ret && foundDevs.size() == 0)
425 {
426 foundDevs.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700427 boost::container::flat_map<std::string, BasicVariantType>());
James Feist3cb5fec2018-01-23 14:41:51 -0800428 }
James Feist6bd2a022018-03-13 12:30:58 -0700429 if (matchOne && foundDevs.size() > 1)
430 {
431 foundDevs.erase(foundDevs.begin() + 1, foundDevs.end());
432 }
James Feist3cb5fec2018-01-23 14:41:51 -0800433 return ret;
434}
James Feist8f2710a2018-05-09 17:18:55 -0700435// this class finds the needed dbus fields and on destruction runs the probe
436struct PerformProbe : std::enable_shared_from_this<PerformProbe>
437{
James Feist3cb5fec2018-01-23 14:41:51 -0800438
James Feist8f2710a2018-05-09 17:18:55 -0700439 PerformProbe(
440 const std::vector<std::string> &probeCommand,
441 std::function<void(std::vector<boost::container::flat_map<
442 std::string, BasicVariantType>> &)> &&callback) :
443 _probeCommand(probeCommand),
444 _callback(std::move(callback))
445 {
446 }
447 ~PerformProbe()
448 {
449 if (probe(_probeCommand, _foundDevs))
450 {
451 _callback(_foundDevs);
452 }
453 }
454 void run()
455 {
456 // parse out dbus probes by discarding other probe types
457 boost::container::flat_map<const char *, probe_type_codes,
458 cmp_str>::const_iterator probeType;
459
460 std::vector<std::string> dbusProbes;
461 for (std::string &probe : _probeCommand)
462 {
463 bool found = false;
464 boost::container::flat_map<const char *, probe_type_codes,
465 cmp_str>::const_iterator probeType;
466 for (probeType = PROBE_TYPES.begin();
467 probeType != PROBE_TYPES.end(); probeType++)
468 {
469 if (probe.find(probeType->first) != std::string::npos)
470 {
471 found = true;
472 break;
473 }
474 }
475 if (found)
476 {
477 continue;
478 }
479 // syntax requires probe before first open brace
480 auto findStart = probe.find("(");
481 std::string interface = probe.substr(0, findStart);
482
483 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
484 }
485 }
486 std::vector<std::string> _probeCommand;
487 std::function<void(
488 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
489 &)>
490 _callback;
491 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
492 _foundDevs;
493};
494
495// writes output files to persist data
James Feistbb43d022018-06-12 15:44:33 -0700496bool writeJsonFiles(const nlohmann::json &systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800497{
498 std::experimental::filesystem::create_directory(OUTPUT_DIR);
499 std::ofstream output(std::string(OUTPUT_DIR) + "system.json");
James Feistbb43d022018-06-12 15:44:33 -0700500 if (!output.good())
501 {
502 return false;
503 }
James Feist1b2e2242018-01-30 13:45:19 -0800504 output << systemConfiguration.dump(4);
505 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700506 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700507}
James Feist1b2e2242018-01-30 13:45:19 -0800508
James Feist8f2710a2018-05-09 17:18:55 -0700509// template function to add array as dbus property
510template <typename PropertyType>
511void addArrayToDbus(const std::string &name, const nlohmann::json &array,
James Feistbb43d022018-06-12 15:44:33 -0700512 sdbusplus::asio::dbus_interface *iface,
513 sdbusplus::asio::PropertyPermission permission)
James Feist8f2710a2018-05-09 17:18:55 -0700514{
515 std::vector<PropertyType> values;
516 for (const auto &property : array)
James Feist1b2e2242018-01-30 13:45:19 -0800517 {
James Feist8f2710a2018-05-09 17:18:55 -0700518 auto ptr = property.get_ptr<const PropertyType *>();
519 if (ptr != nullptr)
James Feist1b2e2242018-01-30 13:45:19 -0800520 {
James Feist8f2710a2018-05-09 17:18:55 -0700521 values.emplace_back(*ptr);
James Feist1b2e2242018-01-30 13:45:19 -0800522 }
523 }
James Feistbb43d022018-06-12 15:44:33 -0700524 // todo(james), currently there are no reason to persist arrays, get around
525 // to it if needed
526
527 iface->register_property(name, values, permission);
James Feist1b2e2242018-01-30 13:45:19 -0800528}
James Feist97a63f12018-05-17 13:50:57 -0700529
530template <typename JsonType>
James Feistbb43d022018-06-12 15:44:33 -0700531bool setJsonFromPointer(const std::string &ptrStr, const JsonType &value,
James Feist97a63f12018-05-17 13:50:57 -0700532 nlohmann::json &systemConfiguration)
533{
534 try
535 {
536 nlohmann::json::json_pointer ptr(ptrStr);
537 nlohmann::json &ref = systemConfiguration[ptr];
538 ref = value;
539 return true;
540 }
541 catch (const std::out_of_range)
542 {
543 return false;
544 }
545}
James Feistbb43d022018-06-12 15:44:33 -0700546
547template <typename PropertyType>
548void addProperty(const std::string &propertyName, const PropertyType &value,
549 sdbusplus::asio::dbus_interface *iface,
550 nlohmann::json &systemConfiguration,
551 const std::string &jsonPointerString,
552 sdbusplus::asio::PropertyPermission permission)
553{
554 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
555 {
556 iface->register_property(propertyName, value);
557 return;
558 }
James Feist0de40152018-07-25 11:56:12 -0700559 iface->register_property(propertyName, value, [
560 &systemConfiguration, jsonPointerString{std::string(jsonPointerString)}
561 ](const PropertyType &newVal, PropertyType &val) {
562 val = newVal;
563 if (!setJsonFromPointer(jsonPointerString, val, systemConfiguration))
564 {
565 std::cerr << "error setting json field\n";
James Feistbb43d022018-06-12 15:44:33 -0700566 return -1;
James Feist0de40152018-07-25 11:56:12 -0700567 }
568 if (writeJsonFiles(systemConfiguration))
569 {
570 std::cerr << "error setting json file\n";
571 return 1;
572 }
573 return -1;
574 });
James Feistbb43d022018-06-12 15:44:33 -0700575}
576
James Feist1b2e2242018-01-30 13:45:19 -0800577// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700578void populateInterfaceFromJson(
579 nlohmann::json &systemConfiguration, const std::string &jsonPointerPath,
580 sdbusplus::asio::dbus_interface *iface, nlohmann::json &dict,
581 sdbusplus::asio::object_server &objServer,
582 sdbusplus::asio::PropertyPermission permission =
583 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800584{
James Feist9eb0b582018-04-27 12:15:46 -0700585 for (auto &dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800586 {
James Feist8f2710a2018-05-09 17:18:55 -0700587 auto type = dictPair.value().type();
588 bool array = false;
589 if (dictPair.value().type() == nlohmann::json::value_t::array)
590 {
591 array = true;
592 if (!dictPair.value().size())
593 {
594 continue;
595 }
596 type = dictPair.value()[0].type();
597 bool isLegal = true;
598 for (const auto &arrayItem : dictPair.value())
599 {
600 if (arrayItem.type() != type)
601 {
602 isLegal = false;
603 break;
604 }
605 }
606 if (!isLegal)
607 {
608 std::cerr << "dbus format error" << dictPair.value() << "\n";
609 continue;
610 }
611 if (type == nlohmann::json::value_t::object)
612 {
613 continue; // handled elsewhere
614 }
615 }
James Feist97a63f12018-05-17 13:50:57 -0700616 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700617 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
618 {
619 // all setable numbers are doubles as it is difficult to always
620 // create a configuration file with all whole numbers as decimals
621 // i.e. 1.0
622 if (dictPair.value().is_number())
623 {
624 type = nlohmann::json::value_t::number_float;
625 }
626 }
627
James Feist8f2710a2018-05-09 17:18:55 -0700628 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800629 {
James Feist9eb0b582018-04-27 12:15:46 -0700630 case (nlohmann::json::value_t::boolean):
631 {
James Feist8f2710a2018-05-09 17:18:55 -0700632 if (array)
633 {
634 // todo: array of bool isn't detected correctly by
635 // sdbusplus, change it to numbers
636 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700637 iface, permission);
James Feist8f2710a2018-05-09 17:18:55 -0700638 }
James Feistbb43d022018-06-12 15:44:33 -0700639
James Feist97a63f12018-05-17 13:50:57 -0700640 else
641 {
James Feistbb43d022018-06-12 15:44:33 -0700642 addProperty(dictPair.key(), dictPair.value().get<bool>(),
643 iface, systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700644 }
James Feist9eb0b582018-04-27 12:15:46 -0700645 break;
646 }
647 case (nlohmann::json::value_t::number_integer):
648 {
James Feist8f2710a2018-05-09 17:18:55 -0700649 if (array)
650 {
651 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700652 iface, permission);
James Feist97a63f12018-05-17 13:50:57 -0700653 }
654 else
655 {
James Feistbb43d022018-06-12 15:44:33 -0700656 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
657 iface, systemConfiguration, key,
658 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700659 }
James Feist9eb0b582018-04-27 12:15:46 -0700660 break;
661 }
662 case (nlohmann::json::value_t::number_unsigned):
663 {
James Feist8f2710a2018-05-09 17:18:55 -0700664 if (array)
665 {
666 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700667 iface, permission);
James Feist97a63f12018-05-17 13:50:57 -0700668 }
669 else
670 {
James Feistbb43d022018-06-12 15:44:33 -0700671 addProperty(dictPair.key(),
672 dictPair.value().get<uint64_t>(), iface,
673 systemConfiguration, key,
674 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700675 }
James Feist9eb0b582018-04-27 12:15:46 -0700676 break;
677 }
678 case (nlohmann::json::value_t::number_float):
679 {
James Feist8f2710a2018-05-09 17:18:55 -0700680 if (array)
681 {
682 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700683 iface, permission);
James Feist8f2710a2018-05-09 17:18:55 -0700684 }
James Feistbb43d022018-06-12 15:44:33 -0700685
James Feist97a63f12018-05-17 13:50:57 -0700686 else
687 {
James Feistbb43d022018-06-12 15:44:33 -0700688 addProperty(dictPair.key(), dictPair.value().get<double>(),
689 iface, systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700690 }
James Feist9eb0b582018-04-27 12:15:46 -0700691 break;
692 }
693 case (nlohmann::json::value_t::string):
694 {
James Feist8f2710a2018-05-09 17:18:55 -0700695 if (array)
696 {
James Feistbb43d022018-06-12 15:44:33 -0700697 addArrayToDbus<std::string>(
698 dictPair.key(), dictPair.value(), iface, permission);
James Feist97a63f12018-05-17 13:50:57 -0700699 }
700 else
701 {
James Feistbb43d022018-06-12 15:44:33 -0700702 addProperty(dictPair.key(),
703 dictPair.value().get<std::string>(), iface,
704 systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700705 }
James Feist9eb0b582018-04-27 12:15:46 -0700706 break;
707 }
James Feist1b2e2242018-01-30 13:45:19 -0800708 }
709 }
James Feist1b2e2242018-01-30 13:45:19 -0800710
James Feist8f2710a2018-05-09 17:18:55 -0700711 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800712}
713
James Feist97a63f12018-05-17 13:50:57 -0700714void postToDbus(const nlohmann::json &newConfiguration,
715 nlohmann::json &systemConfiguration,
James Feist8f2710a2018-05-09 17:18:55 -0700716 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800717
James Feist1b2e2242018-01-30 13:45:19 -0800718{
James Feist97a63f12018-05-17 13:50:57 -0700719 // iterate through boards
720 for (auto &boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800721 {
722 std::string boardKey = boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700723 std::vector<std::string> path;
724 std::string jsonPointerPath = "/" + boardKey;
725 // loop through newConfiguration, but use values from system
726 // configuration to be able to modify via dbus later
727 auto boardValues = systemConfiguration[boardKey];
James Feistd63d18a2018-07-19 15:23:45 -0700728 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800729 std::string boardType;
730 if (findBoardType != boardValues.end() &&
731 findBoardType->type() == nlohmann::json::value_t::string)
732 {
733 boardType = findBoardType->get<std::string>();
734 std::regex_replace(boardType.begin(), boardType.begin(),
735 boardType.end(), ILLEGAL_DBUS_REGEX, "_");
736 }
737 else
738 {
739 std::cerr << "Unable to find type for " << boardKey
740 << " reverting to Chassis.\n";
741 boardType = "Chassis";
742 }
James Feist11be6672018-04-06 14:05:32 -0700743 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800744
745 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
746 ILLEGAL_DBUS_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700747 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
748 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800749
James Feist8f2710a2018-05-09 17:18:55 -0700750 auto inventoryIface = objServer.add_interface(
751 boardName, "xyz.openbmc_project.Inventory.Item");
752 auto boardIface = objServer.add_interface(
753 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700754
James Feist97a63f12018-05-17 13:50:57 -0700755 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
756 boardIface.get(), boardValues, objServer);
757 jsonPointerPath += "/";
758 // iterate through board properties
James Feist9eb0b582018-04-27 12:15:46 -0700759 for (auto &boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700760 {
761 if (boardField.value().type() == nlohmann::json::value_t::object)
762 {
James Feist8f2710a2018-05-09 17:18:55 -0700763 auto iface =
764 objServer.add_interface(boardName, boardField.key());
James Feist97a63f12018-05-17 13:50:57 -0700765 populateInterfaceFromJson(
766 systemConfiguration, jsonPointerPath + boardField.key(),
767 iface.get(), boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700768 }
769 }
James Feist97a63f12018-05-17 13:50:57 -0700770
James Feist1e3e6982018-08-03 16:09:28 -0700771 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800772 if (exposes == boardValues.end())
773 {
774 continue;
775 }
James Feist97a63f12018-05-17 13:50:57 -0700776 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700777 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700778
779 // store the board level pointer so we can modify it on the way down
780 std::string jsonPointerPathBoard = jsonPointerPath;
781 size_t exposesIndex = -1;
James Feist1b2e2242018-01-30 13:45:19 -0800782 for (auto &item : *exposes)
783 {
James Feist97a63f12018-05-17 13:50:57 -0700784 exposesIndex++;
785 jsonPointerPath = jsonPointerPathBoard;
786 jsonPointerPath += std::to_string(exposesIndex);
787
James Feistd63d18a2018-07-19 15:23:45 -0700788 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800789 if (findName == item.end())
790 {
791 std::cerr << "cannot find name in field " << item << "\n";
792 continue;
793 }
James Feist1e3e6982018-08-03 16:09:28 -0700794 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800795 // if status is not found it is assumed to be status = 'okay'
796 if (findStatus != item.end())
797 {
798 if (*findStatus == "disabled")
799 {
800 continue;
801 }
802 }
James Feistd63d18a2018-07-19 15:23:45 -0700803 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800804 std::string itemType;
805 if (findType != item.end())
806 {
807 itemType = findType->get<std::string>();
808 std::regex_replace(itemType.begin(), itemType.begin(),
809 itemType.end(), ILLEGAL_DBUS_REGEX, "_");
810 }
811 else
812 {
813 itemType = "unknown";
814 }
815 std::string itemName = findName->get<std::string>();
816 std::regex_replace(itemName.begin(), itemName.begin(),
817 itemName.end(), ILLEGAL_DBUS_REGEX, "_");
James Feist8f2710a2018-05-09 17:18:55 -0700818 auto itemIface = objServer.add_interface(
819 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800820 "xyz.openbmc_project.Configuration." + itemType);
821
James Feist97a63f12018-05-17 13:50:57 -0700822 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
823 itemIface.get(), item, objServer);
James Feist1b2e2242018-01-30 13:45:19 -0800824
James Feist9eb0b582018-04-27 12:15:46 -0700825 for (auto &objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800826 {
James Feist97a63f12018-05-17 13:50:57 -0700827 jsonPointerPath = jsonPointerPathBoard +
828 std::to_string(exposesIndex) + "/" +
829 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -0800830 if (objectPair.value().type() ==
831 nlohmann::json::value_t::object)
832 {
James Feist8f2710a2018-05-09 17:18:55 -0700833 auto objectIface = objServer.add_interface(
834 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800835 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -0700836 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -0700837
838 populateInterfaceFromJson(
839 systemConfiguration, jsonPointerPath, objectIface.get(),
840 objectPair.value(), objServer);
James Feist1b2e2242018-01-30 13:45:19 -0800841 }
842 else if (objectPair.value().type() ==
843 nlohmann::json::value_t::array)
844 {
845 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -0700846 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -0800847 {
James Feist8f2710a2018-05-09 17:18:55 -0700848 continue;
849 }
850 bool isLegal = true;
851 auto type = objectPair.value()[0].type();
852 if (type != nlohmann::json::value_t::object)
853 {
854 continue;
855 }
856
857 // verify legal json
858 for (const auto &arrayItem : objectPair.value())
859 {
860 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800861 {
James Feist8f2710a2018-05-09 17:18:55 -0700862 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800863 break;
864 }
James Feist8f2710a2018-05-09 17:18:55 -0700865 }
866 if (!isLegal)
867 {
868 std::cerr << "dbus format error" << objectPair.value()
869 << "\n";
870 break;
871 }
872
873 for (auto &arrayItem : objectPair.value())
874 {
James Feistbb43d022018-06-12 15:44:33 -0700875 // limit what interfaces accept set for safety
876 auto permission =
877 std::find(SETTABLE_INTERFACES.begin(),
878 SETTABLE_INTERFACES.end(),
879 objectPair.key()) !=
880 SETTABLE_INTERFACES.end()
881 ? sdbusplus::asio::PropertyPermission::readWrite
882 : sdbusplus::asio::PropertyPermission::readOnly;
James Feist97a63f12018-05-17 13:50:57 -0700883
James Feist8f2710a2018-05-09 17:18:55 -0700884 auto objectIface = objServer.add_interface(
885 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800886 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -0700887 "." + objectPair.key() + std::to_string(index));
888 populateInterfaceFromJson(systemConfiguration,
889 jsonPointerPath + "/" +
890 std::to_string(index),
891 objectIface.get(), arrayItem,
892 objServer, permission);
893 index++;
James Feist1b2e2242018-01-30 13:45:19 -0800894 }
895 }
896 }
897 }
898 }
899}
900
901// finds the template character (currently set to $) and replaces the value with
902// the field found in a dbus object i.e. $ADDRESS would get populated with the
903// ADDRESS field from a object on dbus
904void templateCharReplace(
905 nlohmann::json::iterator &keyPair,
James Feist8f2710a2018-05-09 17:18:55 -0700906 const boost::container::flat_map<std::string, BasicVariantType>
James Feist1b2e2242018-01-30 13:45:19 -0800907 &foundDevice,
908 size_t &foundDeviceIdx)
909{
James Feist11be6672018-04-06 14:05:32 -0700910 if (keyPair.value().type() == nlohmann::json::value_t::object)
911 {
912 for (auto nextLayer = keyPair.value().begin();
913 nextLayer != keyPair.value().end(); nextLayer++)
914 {
915 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
916 }
917 return;
918 }
919 else if (keyPair.value().type() != nlohmann::json::value_t::string)
James Feist1b2e2242018-01-30 13:45:19 -0800920 {
921 return;
922 }
923
924 std::string value = keyPair.value();
925 if (value.find(TEMPLATE_CHAR) != std::string::npos)
926 {
927 std::string templateValue = value;
928
929 templateValue.erase(0, 1); // remove template character
930
931 // special case index
932 if ("index" == templateValue)
933 {
934 keyPair.value() = foundDeviceIdx;
935 }
936 else
937 {
James Feist13b86d62018-05-29 11:24:35 -0700938 bool found = false;
James Feist1b2e2242018-01-30 13:45:19 -0800939 for (auto &foundDevicePair : foundDevice)
940 {
941 if (boost::iequals(foundDevicePair.first, templateValue))
942 {
James Feist13b86d62018-05-29 11:24:35 -0700943 mapbox::util::apply_visitor(
944 [&](auto &&val) { keyPair.value() = val; },
945 foundDevicePair.second);
946 found = true;
James Feist1b2e2242018-01-30 13:45:19 -0800947 break;
948 }
949 }
James Feist13b86d62018-05-29 11:24:35 -0700950 if (!found)
James Feist1b2e2242018-01-30 13:45:19 -0800951 {
952 std::cerr << "could not find symbol " << templateValue << "\n";
953 }
James Feist1b2e2242018-01-30 13:45:19 -0800954 }
955 }
956}
957
James Feist8f2710a2018-05-09 17:18:55 -0700958// reads json files out of the filesystem
959bool findJsonFiles(std::list<nlohmann::json> &configurations)
James Feist3cb5fec2018-01-23 14:41:51 -0800960{
961 // find configuration files
962 std::vector<fs::path> jsonPaths;
963 if (!find_files(fs::path(CONFIGURATION_DIR), R"(.*\.json)", jsonPaths, 0))
964 {
965 std::cerr << "Unable to find any configuration files in "
966 << CONFIGURATION_DIR << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -0800967 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800968 }
James Feist3cb5fec2018-01-23 14:41:51 -0800969 for (auto &jsonPath : jsonPaths)
970 {
James Feist1e3e6982018-08-03 16:09:28 -0700971 if (boost::algorithm::ends_with(jsonPath.string(), schemaFile))
972 {
973 // todo: parse using schema
974 continue;
975 }
James Feist3cb5fec2018-01-23 14:41:51 -0800976 std::ifstream jsonStream(jsonPath.c_str());
977 if (!jsonStream.good())
978 {
979 std::cerr << "unable to open " << jsonPath.string() << "\n";
980 continue;
981 }
982 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
983 if (data.is_discarded())
984 {
985 std::cerr << "syntax error in " << jsonPath.string() << "\n";
986 continue;
987 }
988 if (data.type() == nlohmann::json::value_t::array)
989 {
990 for (auto &d : data)
991 {
992 configurations.emplace_back(d);
993 }
994 }
995 else
996 {
997 configurations.emplace_back(data);
998 }
999 }
James Feist75fdeeb2018-02-20 14:26:16 -08001000}
James Feist3cb5fec2018-01-23 14:41:51 -08001001
James Feist8f2710a2018-05-09 17:18:55 -07001002struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001003{
James Feist75fdeeb2018-02-20 14:26:16 -08001004
James Feist8f2710a2018-05-09 17:18:55 -07001005 PerformScan(nlohmann::json &systemConfiguration,
1006 std::list<nlohmann::json> &configurations,
1007 std::function<void(void)> &&callback) :
1008 _systemConfiguration(systemConfiguration),
1009 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001010 {
James Feist8f2710a2018-05-09 17:18:55 -07001011 }
1012 void run()
1013 {
1014 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001015 {
James Feist1e3e6982018-08-03 16:09:28 -07001016 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001017 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001018
James Feist1b2e2242018-01-30 13:45:19 -08001019 nlohmann::json probeCommand;
1020 // check for poorly formatted fields, probe must be an array
1021 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001022 {
1023 std::cerr << "configuration file missing probe:\n " << *it
1024 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001025 it = _configurations.erase(it);
1026 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001027 }
James Feist1b2e2242018-01-30 13:45:19 -08001028 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001029 {
1030 probeCommand = nlohmann::json::array();
1031 probeCommand.push_back(*findProbe);
1032 }
1033 else
1034 {
1035 probeCommand = *findProbe;
1036 }
James Feist1b2e2242018-01-30 13:45:19 -08001037
1038 if (findName == it->end())
1039 {
1040 std::cerr << "configuration file missing name:\n " << *it
1041 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001042 it = _configurations.erase(it);
1043 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001044 }
James Feist8f2710a2018-05-09 17:18:55 -07001045 std::string name = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001046
James Feist8f2710a2018-05-09 17:18:55 -07001047 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(), name) !=
1048 PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001049 {
James Feist8f2710a2018-05-09 17:18:55 -07001050 it = _configurations.erase(it);
1051 continue;
1052 }
1053 nlohmann::json *record = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001054
James Feist8f2710a2018-05-09 17:18:55 -07001055 // store reference to this to children to makes sure we don't get
1056 // destroyed too early
1057 auto thisRef = shared_from_this();
1058 auto p = std::make_shared<PerformProbe>(
1059 probeCommand,
1060 [&, record, name,
1061 thisRef](std::vector<boost::container::flat_map<
1062 std::string, BasicVariantType>> &foundDevices) {
1063 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001064
James Feist8f2710a2018-05-09 17:18:55 -07001065 PASSED_PROBES.push_back(name);
1066 size_t foundDeviceIdx = 0;
1067
James Feistbe5425f2018-06-08 10:30:55 -07001068 // insert into configuration temporarly to be able to
1069 // reference ourselves
1070 _systemConfiguration[name] = *record;
1071
James Feist8f2710a2018-05-09 17:18:55 -07001072 for (auto &foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001073 {
James Feist8f2710a2018-05-09 17:18:55 -07001074 for (auto keyPair = record->begin();
1075 keyPair != record->end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001076 {
James Feist1b2e2242018-01-30 13:45:19 -08001077 templateCharReplace(keyPair, foundDevice,
1078 foundDeviceIdx);
James Feist8f2710a2018-05-09 17:18:55 -07001079 }
James Feist1e3e6982018-08-03 16:09:28 -07001080 auto findExpose = record->find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001081 if (findExpose == record->end())
1082 {
1083 continue;
1084 }
1085 for (auto &expose : *findExpose)
1086 {
1087 for (auto keyPair = expose.begin();
1088 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001089 {
James Feist1b2e2242018-01-30 13:45:19 -08001090
James Feist8f2710a2018-05-09 17:18:55 -07001091 // fill in template characters with devices
1092 // found
1093 templateCharReplace(keyPair, foundDevice,
1094 foundDeviceIdx);
1095 // special case bind
James Feist1e3e6982018-08-03 16:09:28 -07001096 if (boost::starts_with(keyPair.key(), "Bind"))
James Feist8f2710a2018-05-09 17:18:55 -07001097 {
1098 if (keyPair.value().type() !=
1099 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001100 {
James Feist8f2710a2018-05-09 17:18:55 -07001101 std::cerr << "bind_ value must be of "
1102 "type string "
1103 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001104 continue;
1105 }
James Feist8f2710a2018-05-09 17:18:55 -07001106 bool foundBind = false;
1107 std::string bind = keyPair.key().substr(
James Feist1e3e6982018-08-03 16:09:28 -07001108 sizeof("Bind") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001109
James Feist8f2710a2018-05-09 17:18:55 -07001110 for (auto &configurationPair :
1111 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001112 {
James Feist1b2e2242018-01-30 13:45:19 -08001113
James Feist8f2710a2018-05-09 17:18:55 -07001114 auto configListFind =
1115 configurationPair.value().find(
James Feist1e3e6982018-08-03 16:09:28 -07001116 "Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001117
1118 if (configListFind ==
1119 configurationPair.value()
1120 .end() ||
1121 configListFind->type() !=
1122 nlohmann::json::value_t::array)
1123 {
1124 continue;
1125 }
1126 for (auto &exposedObject :
1127 *configListFind)
1128 {
1129 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001130 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001131 if (boost::iequals(
1132 foundObjectName,
1133 keyPair.value()
1134 .get<std::string>()))
1135 {
James Feist1e3e6982018-08-03 16:09:28 -07001136 exposedObject["Status"] =
James Feist8f2710a2018-05-09 17:18:55 -07001137 "okay";
1138 expose[bind] = exposedObject;
1139
1140 foundBind = true;
1141 break;
1142 }
1143 }
1144 if (foundBind)
1145 {
James Feist3cb5fec2018-01-23 14:41:51 -08001146 break;
1147 }
1148 }
James Feist8f2710a2018-05-09 17:18:55 -07001149 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001150 {
James Feist8f2710a2018-05-09 17:18:55 -07001151 std::cerr << "configuration file "
1152 "dependency error, "
1153 "could not find bind "
1154 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001155 }
1156 }
1157 }
1158 }
1159 }
James Feistbe5425f2018-06-08 10:30:55 -07001160 // overwrite ourselves with cleaned up version
James Feist8f2710a2018-05-09 17:18:55 -07001161 _systemConfiguration[name] = *record;
1162 });
1163 p->run();
1164 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001165 }
1166 }
James Feist75fdeeb2018-02-20 14:26:16 -08001167
James Feist8f2710a2018-05-09 17:18:55 -07001168 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001169 {
James Feist8f2710a2018-05-09 17:18:55 -07001170 if (_passed)
1171 {
1172 auto nextScan = std::make_shared<PerformScan>(
1173 _systemConfiguration, _configurations, std::move(_callback));
1174 nextScan->run();
1175 }
1176 else
1177 {
1178 _callback();
1179 }
1180 }
1181 nlohmann::json &_systemConfiguration;
1182 std::list<nlohmann::json> _configurations;
1183 std::function<void(void)> _callback;
1184 std::vector<std::shared_ptr<PerformProbe>> _probes;
1185 bool _passed = false;
1186};
James Feistc95cb142018-02-26 10:41:42 -08001187
James Feist8f2710a2018-05-09 17:18:55 -07001188// main properties changed entry
1189void propertiesChangedCallback(
1190 boost::asio::io_service &io,
1191 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1192 nlohmann::json &systemConfiguration,
1193 sdbusplus::asio::object_server &objServer)
1194{
1195 static boost::asio::deadline_timer timer(io);
1196 timer.expires_from_now(boost::posix_time::seconds(1));
1197
1198 // setup an async wait as we normally get flooded with new requests
1199 timer.async_wait([&](const boost::system::error_code &ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001200 if (ec == boost::asio::error::operation_aborted)
1201 {
1202 // we were cancelled
1203 return;
1204 }
1205 else if (ec)
1206 {
1207 std::cerr << "async wait error " << ec << "\n";
1208 return;
1209 }
1210
1211 nlohmann::json oldConfiguration = systemConfiguration;
1212 DBUS_PROBE_OBJECTS.clear();
1213
1214 std::list<nlohmann::json> configurations;
1215 if (!findJsonFiles(configurations))
1216 {
1217 std::cerr << "cannot find json files\n";
1218 return;
1219 }
1220
1221 auto perfScan = std::make_shared<PerformScan>(
1222 systemConfiguration, configurations, [&, oldConfiguration]() {
1223 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001224 for (auto it = newConfiguration.begin();
1225 it != newConfiguration.end();)
1226 {
1227 auto findKey = oldConfiguration.find(it.key());
1228 if (findKey != oldConfiguration.end())
1229 {
1230 it = newConfiguration.erase(it);
1231 }
1232 else
1233 {
1234 it++;
1235 }
1236 }
James Feist8f2710a2018-05-09 17:18:55 -07001237 registerCallbacks(io, dbusMatches, systemConfiguration,
1238 objServer);
1239 io.post([&, newConfiguration]() {
1240 // todo: for now, only add new configurations,
1241 // unload to come later unloadOverlays();
1242 loadOverlays(newConfiguration);
James Feistbb43d022018-06-12 15:44:33 -07001243 io.post([&]() {
1244 if (!writeJsonFiles(systemConfiguration))
1245 {
1246 std::cerr << "Error writing json files\n";
1247 }
1248 });
James Feist8f2710a2018-05-09 17:18:55 -07001249 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001250 postToDbus(newConfiguration, systemConfiguration,
1251 objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001252 });
1253 });
1254 });
1255 perfScan->run();
1256 });
James Feist75fdeeb2018-02-20 14:26:16 -08001257}
1258
James Feist8f2710a2018-05-09 17:18:55 -07001259void registerCallbacks(boost::asio::io_service &io,
1260 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1261 nlohmann::json &systemConfiguration,
1262 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001263{
1264 static boost::container::flat_set<std::string> watchedObjects;
1265
1266 for (const auto &objectMap : DBUS_PROBE_OBJECTS)
1267 {
1268 auto findObject = watchedObjects.find(objectMap.first);
1269 if (findObject != watchedObjects.end())
1270 {
1271 continue;
1272 }
James Feist8f2710a2018-05-09 17:18:55 -07001273 std::function<void(sdbusplus::message::message & message)>
1274 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001275
James Feist8f2710a2018-05-09 17:18:55 -07001276 [&](sdbusplus::message::message &) {
1277 propertiesChangedCallback(io, dbusMatches,
1278 systemConfiguration, objServer);
1279 };
1280
1281 sdbusplus::bus::match::match match(
1282 static_cast<sdbusplus::bus::bus &>(*SYSTEM_BUS),
1283 "type='signal',member='PropertiesChanged',arg0='" +
1284 objectMap.first + "'",
1285 eventHandler);
1286 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001287 }
1288}
1289
1290int main(int argc, char **argv)
1291{
1292 // setup connection to dbus
1293 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001294 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001295 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001296
James Feist8f2710a2018-05-09 17:18:55 -07001297 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001298
James Feist8f2710a2018-05-09 17:18:55 -07001299 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1300 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1301 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001302
James Feist8f2710a2018-05-09 17:18:55 -07001303 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1304 objServer.add_interface("/xyz/openbmc_project/inventory",
1305 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001306
1307 // to keep reference to the match / filter objects so they don't get
1308 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001309 std::vector<sdbusplus::bus::match::match> dbusMatches;
1310
1311 nlohmann::json systemConfiguration = nlohmann::json::object();
1312
1313 inventoryIface->register_method(
1314 "Notify", [](const boost::container::flat_map<
1315 std::string,
1316 boost::container::flat_map<std::string, BasicVariantType>>
1317 &object) { return; });
1318 inventoryIface->initialize();
1319
1320 io.post([&]() {
1321 unloadAllOverlays();
1322 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1323 objServer);
1324 });
James Feist4131aea2018-03-09 09:47:30 -08001325
James Feistfd1264a2018-05-03 12:10:00 -07001326 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001327 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1328 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001329 });
James Feist8f2710a2018-05-09 17:18:55 -07001330 entityIface->initialize();
1331
James Feist1b2e2242018-01-30 13:45:19 -08001332 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001333
1334 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001335}