blob: 36478d75bf1fd80a29a1b897b0183df9dce8da2a [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";
37constexpr const char *TEMPLATE_CHAR = "$";
James Feistc95cb142018-02-26 10:41:42 -080038constexpr const size_t PROPERTIES_CHANGED_UNTIL_FLUSH_COUNT = 20;
James Feist8f2710a2018-05-09 17:18:55 -070039constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist4131aea2018-03-09 09:47:30 -080040constexpr const size_t SLEEP_AFTER_PROPERTIES_CHANGE_SECONDS = 5;
James Feist3cb5fec2018-01-23 14:41:51 -080041
42namespace fs = std::experimental::filesystem;
43struct cmp_str
44{
45 bool operator()(const char *a, const char *b) const
46 {
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};
63const static boost::container::flat_map<const char *, probe_type_codes, cmp_str>
64 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 Feist97a63f12018-05-17 13:50:57 -070071static constexpr std::array<const char *, 1> SETTABLE_INTERFACES = {
72 "thresholds"};
73
James Feist8f2710a2018-05-09 17:18:55 -070074using BasicVariantType =
75 sdbusplus::message::variant<std::string, int64_t, uint64_t, double, int32_t,
76 uint32_t, int16_t, uint16_t, uint8_t, bool>;
77
James Feist3cb5fec2018-01-23 14:41:51 -080078using GetSubTreeType = std::vector<
79 std::pair<std::string,
80 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
81
82using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070083 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080084 boost::container::flat_map<
85 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070086 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080087
88boost::container::flat_map<
89 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070090 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -080091 DBUS_PROBE_OBJECTS;
92std::vector<std::string> PASSED_PROBES;
93
94// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -070095std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist3cb5fec2018-01-23 14:41:51 -080096
James Feista6750242018-07-16 14:12:27 -070097const std::regex ILLEGAL_DBUS_REGEX("[^A-Za-z0-9_.]");
James Feist1b2e2242018-01-30 13:45:19 -080098
James Feist8f2710a2018-05-09 17:18:55 -070099void registerCallbacks(boost::asio::io_service &io,
100 std::vector<sdbusplus::bus::match::match> &dbusMatches,
101 nlohmann::json &systemConfiguration,
102 sdbusplus::asio::object_server &objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800103
James Feist3cb5fec2018-01-23 14:41:51 -0800104// calls the mapper to find all exposed objects of an interface type
105// and creates a vector<flat_map> that contains all the key value pairs
106// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700107void findDbusObjects(std::shared_ptr<PerformProbe> probe,
108 std::shared_ptr<sdbusplus::asio::connection> connection,
109 std::string &interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800110{
James Feist494155a2018-03-14 16:23:24 -0700111 // todo: this is only static because the mapper is unreliable as of today
112 static boost::container::flat_map<std::string,
113 boost::container::flat_set<std::string>>
114 connections;
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
128 std::array<const char *, 1> objects = {interface.c_str()};
129 std::vector<std::shared_ptr<PerformProbe>> &pending =
130 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(
140 [&, connection, interface](boost::system::error_code &ec,
141 const GetSubTreeType &interfaceSubtree) {
142 auto &interfaceConnections = connections[interface];
143 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700144 {
James Feist8f2710a2018-05-09 17:18:55 -0700145 std::cerr
146 << "Error communicating to mapper, using cached data if "
147 "available\n";
148 if (interfaceConnections.empty())
149 {
James Feistf26422f2018-07-09 10:26:47 -0700150 // if we can't get the mapper data on the first run,
151 // something is very wrong
152 std::exit(EXIT_FAILURE);
James Feist8f2710a2018-05-09 17:18:55 -0700153 }
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 Feist8f2710a2018-05-09 17:18:55 -0700157 for (auto &object : interfaceSubtree)
158 {
159 for (auto &connPair : object.second)
160 {
161 auto insertPair =
162 interfaceConnections.insert(connPair.first);
163 }
164 }
James Feist3cb5fec2018-01-23 14:41:51 -0800165 }
James Feist8f2710a2018-05-09 17:18:55 -0700166 // get managed objects for all interfaces
167 for (const auto &conn : interfaceConnections)
168 {
169 connection->async_method_call(
170 [&, conn,
171 interface](boost::system::error_code &ec,
172 const ManagedObjectType &managedInterface) {
173 if (ec)
174 {
175 std::cerr
176 << "error getting managed object for device "
177 << conn << "\n";
178 pendingProbes[interface].clear();
179 return;
180 }
181 for (auto &interfaceManagedObj : managedInterface)
182 {
183 auto ifaceObjFind =
184 interfaceManagedObj.second.find(interface);
185 if (ifaceObjFind !=
186 interfaceManagedObj.second.end())
187 {
188 std::vector<boost::container::flat_map<
189 std::string, BasicVariantType>>
190 &dbusObject = DBUS_PROBE_OBJECTS[interface];
191 dbusObject.emplace_back(ifaceObjFind->second);
192 }
193 }
194 pendingProbes[interface].clear();
195 },
196 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
197 "GetManagedObjects");
198 }
199 },
200 "xyz.openbmc_project.ObjectMapper",
201 "/xyz/openbmc_project/object_mapper",
202 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "", MAX_MAPPER_DEPTH,
203 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800204}
James Feist8f2710a2018-05-09 17:18:55 -0700205// probes dbus interface dictionary for a key with a value that matches a regex
James Feist3cb5fec2018-01-23 14:41:51 -0800206bool probeDbus(
207 const std::string &interface,
208 const std::map<std::string, nlohmann::json> &matches,
James Feist8f2710a2018-05-09 17:18:55 -0700209 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
James Feist3cb5fec2018-01-23 14:41:51 -0800210 &devices,
211 bool &foundProbe)
212{
James Feist8f2710a2018-05-09 17:18:55 -0700213 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
214 &dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800215 if (dbusObject.empty())
216 {
James Feist8f2710a2018-05-09 17:18:55 -0700217 foundProbe = false;
218 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800219 }
220 foundProbe = true;
221
222 bool foundMatch = false;
223 for (auto &device : dbusObject)
224 {
225 bool deviceMatches = true;
226 for (auto &match : matches)
227 {
228 auto deviceValue = device.find(match.first);
229 if (deviceValue != device.end())
230 {
231 switch (match.second.type())
232 {
James Feist9eb0b582018-04-27 12:15:46 -0700233 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800234 {
James Feist9eb0b582018-04-27 12:15:46 -0700235 std::regex search(match.second.get<std::string>());
236 std::smatch match;
237
238 // convert value to string respresentation
James Feist8f2710a2018-05-09 17:18:55 -0700239 std::string probeValue = mapbox::util::apply_visitor(
240 VariantToStringVisitor(), deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700241 if (!std::regex_search(probeValue, match, search))
242 {
243 deviceMatches = false;
244 break;
245 }
James Feist3cb5fec2018-01-23 14:41:51 -0800246 break;
247 }
James Feist9eb0b582018-04-27 12:15:46 -0700248 case nlohmann::json::value_t::boolean:
249 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800250 {
James Feist8f2710a2018-05-09 17:18:55 -0700251 unsigned int probeValue = mapbox::util::apply_visitor(
James Feist9eb0b582018-04-27 12:15:46 -0700252 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800253
James Feist9eb0b582018-04-27 12:15:46 -0700254 if (probeValue != match.second.get<unsigned int>())
255 {
256 deviceMatches = false;
257 }
258 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800259 }
James Feist9eb0b582018-04-27 12:15:46 -0700260 case nlohmann::json::value_t::number_integer:
261 {
James Feist8f2710a2018-05-09 17:18:55 -0700262 int probeValue = mapbox::util::apply_visitor(
James Feist9eb0b582018-04-27 12:15:46 -0700263 VariantToIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800264
James Feist9eb0b582018-04-27 12:15:46 -0700265 if (probeValue != match.second.get<int>())
266 {
267 deviceMatches = false;
268 }
269 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800270 }
James Feist9eb0b582018-04-27 12:15:46 -0700271 case nlohmann::json::value_t::number_float:
272 {
James Feist8f2710a2018-05-09 17:18:55 -0700273 float probeValue = mapbox::util::apply_visitor(
James Feist9eb0b582018-04-27 12:15:46 -0700274 VariantToFloatVisitor(), deviceValue->second);
275
276 if (probeValue != match.second.get<float>())
277 {
278 deviceMatches = false;
279 }
280 break;
281 }
James Feist3cb5fec2018-01-23 14:41:51 -0800282 }
283 }
284 else
285 {
286 deviceMatches = false;
287 break;
288 }
289 }
290 if (deviceMatches)
291 {
292 devices.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700293 boost::container::flat_map<std::string, BasicVariantType>(
James Feist3cb5fec2018-01-23 14:41:51 -0800294 device));
295 foundMatch = true;
296 deviceMatches = false; // for next iteration
297 }
298 }
299 return foundMatch;
300}
301
302// default probe entry point, iterates a list looking for specific types to
303// call specific probe functions
304bool probe(
James Feist8f2710a2018-05-09 17:18:55 -0700305 const std::vector<std::string> &probeCommand,
306 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
James Feist3cb5fec2018-01-23 14:41:51 -0800307 &foundDevs)
308{
309 const static std::regex command(R"(\((.*)\))");
310 std::smatch match;
311 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700312 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800313 bool cur = true;
314 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
315
316 for (auto &probe : probeCommand)
317 {
318 bool foundProbe = false;
319 boost::container::flat_map<const char *, probe_type_codes,
320 cmp_str>::const_iterator probeType;
321
322 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
323 probeType++)
324 {
325 if (probe.find(probeType->first) != std::string::npos)
326 {
327 foundProbe = true;
328 break;
329 }
330 }
331 if (foundProbe)
332 {
333 switch (probeType->second)
334 {
James Feist9eb0b582018-04-27 12:15:46 -0700335 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800336 {
James Feist8f2710a2018-05-09 17:18:55 -0700337 return false;
James Feist9eb0b582018-04-27 12:15:46 -0700338 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800339 }
James Feist9eb0b582018-04-27 12:15:46 -0700340 case probe_type_codes::TRUE_T:
341 {
James Feist8f2710a2018-05-09 17:18:55 -0700342 return true;
James Feist9eb0b582018-04-27 12:15:46 -0700343 break;
344 }
345 case probe_type_codes::MATCH_ONE:
346 {
347 // set current value to last, this probe type shouldn't
348 // affect the outcome
349 cur = ret;
350 matchOne = true;
351 break;
352 }
353 /*case probe_type_codes::AND:
354 break;
355 case probe_type_codes::OR:
356 break;
357 // these are no-ops until the last command switch
358 */
359 case probe_type_codes::FOUND:
360 {
361 if (!std::regex_search(probe, match, command))
362 {
363 std::cerr << "found probe sytax error " << probe
364 << "\n";
365 return false;
366 }
367 std::string commandStr = *(match.begin() + 1);
368 boost::replace_all(commandStr, "'", "");
369 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
370 commandStr) != PASSED_PROBES.end());
371 break;
372 }
James Feist3cb5fec2018-01-23 14:41:51 -0800373 }
374 }
375 // look on dbus for object
376 else
377 {
378 if (!std::regex_search(probe, match, command))
379 {
380 std::cerr << "dbus probe sytax error " << probe << "\n";
381 return false;
382 }
383 std::string commandStr = *(match.begin() + 1);
384 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700385 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800386 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800387 auto json = nlohmann::json::parse(commandStr, nullptr, false);
388 if (json.is_discarded())
389 {
390 std::cerr << "dbus command sytax error " << commandStr << "\n";
391 return false;
392 }
393 // we can match any (string, variant) property. (string, string)
394 // does a regex
395 std::map<std::string, nlohmann::json> dbusProbeMap =
396 json.get<std::map<std::string, nlohmann::json>>();
397 auto findStart = probe.find("(");
398 if (findStart == std::string::npos)
399 {
400 return false;
401 }
402 std::string probeInterface = probe.substr(0, findStart);
403 cur =
404 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
405 }
406
407 // some functions like AND and OR only take affect after the
408 // fact
409 switch (lastCommand)
410 {
James Feist9eb0b582018-04-27 12:15:46 -0700411 case probe_type_codes::AND:
412 ret = cur && ret;
413 break;
414 case probe_type_codes::OR:
415 ret = cur || ret;
416 break;
417 default:
418 ret = cur;
419 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800420 }
421 lastCommand = probeType != PROBE_TYPES.end()
422 ? probeType->second
423 : probe_type_codes::FALSE_T;
424
425 if (!foundProbe)
426 {
427 std::cerr << "Illegal probe type " << probe << "\n";
428 return false;
429 }
430 }
431
432 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800433 if (ret && foundDevs.size() == 0)
434 {
435 foundDevs.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700436 boost::container::flat_map<std::string, BasicVariantType>());
James Feist3cb5fec2018-01-23 14:41:51 -0800437 }
James Feist6bd2a022018-03-13 12:30:58 -0700438 if (matchOne && foundDevs.size() > 1)
439 {
440 foundDevs.erase(foundDevs.begin() + 1, foundDevs.end());
441 }
James Feist3cb5fec2018-01-23 14:41:51 -0800442 return ret;
443}
James Feist8f2710a2018-05-09 17:18:55 -0700444// this class finds the needed dbus fields and on destruction runs the probe
445struct PerformProbe : std::enable_shared_from_this<PerformProbe>
446{
James Feist3cb5fec2018-01-23 14:41:51 -0800447
James Feist8f2710a2018-05-09 17:18:55 -0700448 PerformProbe(
449 const std::vector<std::string> &probeCommand,
450 std::function<void(std::vector<boost::container::flat_map<
451 std::string, BasicVariantType>> &)> &&callback) :
452 _probeCommand(probeCommand),
453 _callback(std::move(callback))
454 {
455 }
456 ~PerformProbe()
457 {
458 if (probe(_probeCommand, _foundDevs))
459 {
460 _callback(_foundDevs);
461 }
462 }
463 void run()
464 {
465 // parse out dbus probes by discarding other probe types
466 boost::container::flat_map<const char *, probe_type_codes,
467 cmp_str>::const_iterator probeType;
468
469 std::vector<std::string> dbusProbes;
470 for (std::string &probe : _probeCommand)
471 {
472 bool found = false;
473 boost::container::flat_map<const char *, probe_type_codes,
474 cmp_str>::const_iterator probeType;
475 for (probeType = PROBE_TYPES.begin();
476 probeType != PROBE_TYPES.end(); probeType++)
477 {
478 if (probe.find(probeType->first) != std::string::npos)
479 {
480 found = true;
481 break;
482 }
483 }
484 if (found)
485 {
486 continue;
487 }
488 // syntax requires probe before first open brace
489 auto findStart = probe.find("(");
490 std::string interface = probe.substr(0, findStart);
491
492 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
493 }
494 }
495 std::vector<std::string> _probeCommand;
496 std::function<void(
497 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
498 &)>
499 _callback;
500 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
501 _foundDevs;
502};
503
504// writes output files to persist data
505void writeJsonFiles(const nlohmann::json &systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800506{
507 std::experimental::filesystem::create_directory(OUTPUT_DIR);
508 std::ofstream output(std::string(OUTPUT_DIR) + "system.json");
509 output << systemConfiguration.dump(4);
510 output.close();
James Feist8f2710a2018-05-09 17:18:55 -0700511}
James Feist1b2e2242018-01-30 13:45:19 -0800512
James Feist8f2710a2018-05-09 17:18:55 -0700513// template function to add array as dbus property
514template <typename PropertyType>
515void addArrayToDbus(const std::string &name, const nlohmann::json &array,
516 sdbusplus::asio::dbus_interface *iface)
517{
518 std::vector<PropertyType> values;
519 for (const auto &property : array)
James Feist1b2e2242018-01-30 13:45:19 -0800520 {
James Feist8f2710a2018-05-09 17:18:55 -0700521 auto ptr = property.get_ptr<const PropertyType *>();
522 if (ptr != nullptr)
James Feist1b2e2242018-01-30 13:45:19 -0800523 {
James Feist8f2710a2018-05-09 17:18:55 -0700524 values.emplace_back(*ptr);
James Feist1b2e2242018-01-30 13:45:19 -0800525 }
526 }
James Feist8f2710a2018-05-09 17:18:55 -0700527 iface->register_property(name, values);
James Feist1b2e2242018-01-30 13:45:19 -0800528}
James Feist97a63f12018-05-17 13:50:57 -0700529
530template <typename JsonType>
531bool SetJsonFromPointer(const std::string &ptrStr, const JsonType &value,
532 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 Feist1b2e2242018-01-30 13:45:19 -0800546// adds simple json types to interface's properties
James Feist97a63f12018-05-17 13:50:57 -0700547void populateInterfaceFromJson(nlohmann::json &systemConfiguration,
548 const std::string &jsonPointerPath,
James Feist8f2710a2018-05-09 17:18:55 -0700549 sdbusplus::asio::dbus_interface *iface,
550 nlohmann::json &dict,
James Feist97a63f12018-05-17 13:50:57 -0700551 sdbusplus::asio::object_server &objServer,
552 bool setable = false)
James Feist1b2e2242018-01-30 13:45:19 -0800553{
James Feist9eb0b582018-04-27 12:15:46 -0700554 for (auto &dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800555 {
James Feist8f2710a2018-05-09 17:18:55 -0700556 auto type = dictPair.value().type();
557 bool array = false;
558 if (dictPair.value().type() == nlohmann::json::value_t::array)
559 {
560 array = true;
561 if (!dictPair.value().size())
562 {
563 continue;
564 }
565 type = dictPair.value()[0].type();
566 bool isLegal = true;
567 for (const auto &arrayItem : dictPair.value())
568 {
569 if (arrayItem.type() != type)
570 {
571 isLegal = false;
572 break;
573 }
574 }
575 if (!isLegal)
576 {
577 std::cerr << "dbus format error" << dictPair.value() << "\n";
578 continue;
579 }
580 if (type == nlohmann::json::value_t::object)
581 {
582 continue; // handled elsewhere
583 }
584 }
James Feist97a63f12018-05-17 13:50:57 -0700585 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feist8f2710a2018-05-09 17:18:55 -0700586 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800587 {
James Feist9eb0b582018-04-27 12:15:46 -0700588 case (nlohmann::json::value_t::boolean):
589 {
James Feist8f2710a2018-05-09 17:18:55 -0700590 if (array)
591 {
592 // todo: array of bool isn't detected correctly by
593 // sdbusplus, change it to numbers
594 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
595 iface);
596 break;
597 }
James Feist97a63f12018-05-17 13:50:57 -0700598 if (setable)
599 {
600 iface->register_property(
601 std::string(dictPair.key()),
602 dictPair.value().get<bool>(),
603 [&, key](const bool &newVal, bool &val) {
604 val = newVal;
605 if (!SetJsonFromPointer(key, val,
606 systemConfiguration))
607 {
608 std::cerr << "error writing json\n";
609 return -1;
610 }
611 writeJsonFiles(systemConfiguration);
612 return 1;
613 });
614 }
615 else
616 {
617 iface->register_property(std::string(dictPair.key()),
618 dictPair.value().get<bool>());
619 }
James Feist9eb0b582018-04-27 12:15:46 -0700620 break;
621 }
622 case (nlohmann::json::value_t::number_integer):
623 {
James Feist8f2710a2018-05-09 17:18:55 -0700624 if (array)
625 {
626 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
627 iface);
628 break;
629 }
James Feist97a63f12018-05-17 13:50:57 -0700630 if (setable)
631 {
632 iface->register_property(
633 std::string(dictPair.key()),
634 dictPair.value().get<int64_t>(),
635 [&, key](const int64_t &newVal, int64_t &val) {
636 val = newVal;
637 if (!SetJsonFromPointer(key, val,
638 systemConfiguration))
639 {
640 std::cerr << "error writing json\n";
641 return -1;
642 }
643 writeJsonFiles(systemConfiguration);
644 return 1;
645 });
646 }
647 else
648 {
649 iface->register_property(std::string(dictPair.key()),
650 dictPair.value().get<int64_t>());
651 }
James Feist9eb0b582018-04-27 12:15:46 -0700652 break;
653 }
654 case (nlohmann::json::value_t::number_unsigned):
655 {
James Feist8f2710a2018-05-09 17:18:55 -0700656 if (array)
657 {
658 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
659 iface);
660 break;
661 }
James Feist97a63f12018-05-17 13:50:57 -0700662 if (setable)
663 {
664 iface->register_property(
665 std::string(dictPair.key()),
666 dictPair.value().get<uint64_t>(),
667 [&, key](const uint64_t &newVal, uint64_t &val) {
668 val = newVal;
669 if (!SetJsonFromPointer(key, val,
670 systemConfiguration))
671 {
672 std::cerr << "error writing json\n";
673 return -1;
674 }
675 writeJsonFiles(systemConfiguration);
676 return 1;
677 });
678 }
679 else
680 {
681 iface->register_property(std::string(dictPair.key()),
682 dictPair.value().get<uint64_t>());
683 }
James Feist9eb0b582018-04-27 12:15:46 -0700684 break;
685 }
686 case (nlohmann::json::value_t::number_float):
687 {
James Feist8f2710a2018-05-09 17:18:55 -0700688 if (array)
689 {
690 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
691 iface);
692 break;
693 }
James Feist97a63f12018-05-17 13:50:57 -0700694 if (setable)
695 {
696 iface->register_property(
697 std::string(dictPair.key()),
698 dictPair.value().get<double>(),
699 [&, key](const double &newVal, double &val) {
700 val = newVal;
701 if (!SetJsonFromPointer(key, val,
702 systemConfiguration))
703 {
704 std::cerr << "error writing json\n";
705 return -1;
706 }
707 return 1;
708 });
709 }
710 else
711 {
712 iface->register_property(std::string(dictPair.key()),
713 dictPair.value().get<double>());
714 }
James Feist9eb0b582018-04-27 12:15:46 -0700715 break;
716 }
717 case (nlohmann::json::value_t::string):
718 {
James Feist8f2710a2018-05-09 17:18:55 -0700719 if (array)
720 {
721 addArrayToDbus<std::string>(dictPair.key(),
722 dictPair.value(), iface);
723 break;
724 }
James Feist97a63f12018-05-17 13:50:57 -0700725 if (setable)
726 {
727 iface->register_property(
728 std::string(dictPair.key()),
729 dictPair.value().get<std::string>(),
730 [&, key](const std::string &newVal, std::string &val) {
731 val = newVal;
732 if (!SetJsonFromPointer(key, val,
733 systemConfiguration))
734 {
735 std::cerr << "error writing json\n";
736 return -1;
737 }
738 writeJsonFiles(systemConfiguration);
739 return 1;
740 });
741 }
742 else
743 {
744 iface->register_property(
745 std::string(dictPair.key()),
746 dictPair.value().get<std::string>());
747 }
James Feist9eb0b582018-04-27 12:15:46 -0700748 break;
749 }
James Feist1b2e2242018-01-30 13:45:19 -0800750 }
751 }
James Feist1b2e2242018-01-30 13:45:19 -0800752
James Feist8f2710a2018-05-09 17:18:55 -0700753 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800754}
755
James Feist97a63f12018-05-17 13:50:57 -0700756void postToDbus(const nlohmann::json &newConfiguration,
757 nlohmann::json &systemConfiguration,
James Feist8f2710a2018-05-09 17:18:55 -0700758 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800759
James Feist1b2e2242018-01-30 13:45:19 -0800760{
James Feist97a63f12018-05-17 13:50:57 -0700761 // iterate through boards
762 for (auto &boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800763 {
764 std::string boardKey = boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700765 std::vector<std::string> path;
766 std::string jsonPointerPath = "/" + boardKey;
767 // loop through newConfiguration, but use values from system
768 // configuration to be able to modify via dbus later
769 auto boardValues = systemConfiguration[boardKey];
James Feistd63d18a2018-07-19 15:23:45 -0700770 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800771 std::string boardType;
772 if (findBoardType != boardValues.end() &&
773 findBoardType->type() == nlohmann::json::value_t::string)
774 {
775 boardType = findBoardType->get<std::string>();
776 std::regex_replace(boardType.begin(), boardType.begin(),
777 boardType.end(), ILLEGAL_DBUS_REGEX, "_");
778 }
779 else
780 {
781 std::cerr << "Unable to find type for " << boardKey
782 << " reverting to Chassis.\n";
783 boardType = "Chassis";
784 }
James Feist11be6672018-04-06 14:05:32 -0700785 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800786
787 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
788 ILLEGAL_DBUS_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700789 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
790 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800791
James Feist8f2710a2018-05-09 17:18:55 -0700792 auto inventoryIface = objServer.add_interface(
793 boardName, "xyz.openbmc_project.Inventory.Item");
794 auto boardIface = objServer.add_interface(
795 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700796
James Feist97a63f12018-05-17 13:50:57 -0700797 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
798 boardIface.get(), boardValues, objServer);
799 jsonPointerPath += "/";
800 // iterate through board properties
James Feist9eb0b582018-04-27 12:15:46 -0700801 for (auto &boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700802 {
803 if (boardField.value().type() == nlohmann::json::value_t::object)
804 {
James Feist8f2710a2018-05-09 17:18:55 -0700805 auto iface =
806 objServer.add_interface(boardName, boardField.key());
James Feist97a63f12018-05-17 13:50:57 -0700807 populateInterfaceFromJson(
808 systemConfiguration, jsonPointerPath + boardField.key(),
809 iface.get(), boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700810 }
811 }
James Feist97a63f12018-05-17 13:50:57 -0700812
James Feist1b2e2242018-01-30 13:45:19 -0800813 auto exposes = boardValues.find("exposes");
814 if (exposes == boardValues.end())
815 {
816 continue;
817 }
James Feist97a63f12018-05-17 13:50:57 -0700818 // iterate through exposes
819 jsonPointerPath += "exposes/";
820
821 // store the board level pointer so we can modify it on the way down
822 std::string jsonPointerPathBoard = jsonPointerPath;
823 size_t exposesIndex = -1;
James Feist1b2e2242018-01-30 13:45:19 -0800824 for (auto &item : *exposes)
825 {
James Feist97a63f12018-05-17 13:50:57 -0700826 exposesIndex++;
827 jsonPointerPath = jsonPointerPathBoard;
828 jsonPointerPath += std::to_string(exposesIndex);
829
James Feistd63d18a2018-07-19 15:23:45 -0700830 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800831 if (findName == item.end())
832 {
833 std::cerr << "cannot find name in field " << item << "\n";
834 continue;
835 }
836 auto findStatus = item.find("status");
837 // if status is not found it is assumed to be status = 'okay'
838 if (findStatus != item.end())
839 {
840 if (*findStatus == "disabled")
841 {
842 continue;
843 }
844 }
James Feistd63d18a2018-07-19 15:23:45 -0700845 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800846 std::string itemType;
847 if (findType != item.end())
848 {
849 itemType = findType->get<std::string>();
850 std::regex_replace(itemType.begin(), itemType.begin(),
851 itemType.end(), ILLEGAL_DBUS_REGEX, "_");
852 }
853 else
854 {
855 itemType = "unknown";
856 }
857 std::string itemName = findName->get<std::string>();
858 std::regex_replace(itemName.begin(), itemName.begin(),
859 itemName.end(), ILLEGAL_DBUS_REGEX, "_");
James Feist8f2710a2018-05-09 17:18:55 -0700860 auto itemIface = objServer.add_interface(
861 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800862 "xyz.openbmc_project.Configuration." + itemType);
863
James Feist97a63f12018-05-17 13:50:57 -0700864 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
865 itemIface.get(), item, objServer);
James Feist1b2e2242018-01-30 13:45:19 -0800866
James Feist9eb0b582018-04-27 12:15:46 -0700867 for (auto &objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800868 {
James Feist97a63f12018-05-17 13:50:57 -0700869 jsonPointerPath = jsonPointerPathBoard +
870 std::to_string(exposesIndex) + "/" +
871 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -0800872 if (objectPair.value().type() ==
873 nlohmann::json::value_t::object)
874 {
James Feist8f2710a2018-05-09 17:18:55 -0700875 auto objectIface = objServer.add_interface(
876 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800877 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -0700878 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -0700879
880 populateInterfaceFromJson(
881 systemConfiguration, jsonPointerPath, objectIface.get(),
882 objectPair.value(), objServer);
James Feist1b2e2242018-01-30 13:45:19 -0800883 }
884 else if (objectPair.value().type() ==
885 nlohmann::json::value_t::array)
886 {
887 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -0700888 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -0800889 {
James Feist8f2710a2018-05-09 17:18:55 -0700890 continue;
891 }
892 bool isLegal = true;
893 auto type = objectPair.value()[0].type();
894 if (type != nlohmann::json::value_t::object)
895 {
896 continue;
897 }
898
899 // verify legal json
900 for (const auto &arrayItem : objectPair.value())
901 {
902 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800903 {
James Feist8f2710a2018-05-09 17:18:55 -0700904 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800905 break;
906 }
James Feist8f2710a2018-05-09 17:18:55 -0700907 }
908 if (!isLegal)
909 {
910 std::cerr << "dbus format error" << objectPair.value()
911 << "\n";
912 break;
913 }
914
915 for (auto &arrayItem : objectPair.value())
916 {
James Feist97a63f12018-05-17 13:50:57 -0700917 // limit what interfaces accept set for saftey
918 bool setable = std::find(SETTABLE_INTERFACES.begin(),
919 SETTABLE_INTERFACES.end(),
920 objectPair.key()) !=
921 SETTABLE_INTERFACES.end();
922
James Feist8f2710a2018-05-09 17:18:55 -0700923 auto objectIface = objServer.add_interface(
924 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800925 "xyz.openbmc_project.Configuration." + itemType +
James Feist97a63f12018-05-17 13:50:57 -0700926 "." + objectPair.key() +
James Feist8f2710a2018-05-09 17:18:55 -0700927 std::to_string(index++));
James Feist97a63f12018-05-17 13:50:57 -0700928 populateInterfaceFromJson(
929 systemConfiguration,
930 jsonPointerPath + "/" + std::to_string(index),
931 objectIface.get(), arrayItem, objServer, setable);
James Feist1b2e2242018-01-30 13:45:19 -0800932 }
933 }
934 }
935 }
936 }
937}
938
939// finds the template character (currently set to $) and replaces the value with
940// the field found in a dbus object i.e. $ADDRESS would get populated with the
941// ADDRESS field from a object on dbus
942void templateCharReplace(
943 nlohmann::json::iterator &keyPair,
James Feist8f2710a2018-05-09 17:18:55 -0700944 const boost::container::flat_map<std::string, BasicVariantType>
James Feist1b2e2242018-01-30 13:45:19 -0800945 &foundDevice,
946 size_t &foundDeviceIdx)
947{
James Feist11be6672018-04-06 14:05:32 -0700948 if (keyPair.value().type() == nlohmann::json::value_t::object)
949 {
950 for (auto nextLayer = keyPair.value().begin();
951 nextLayer != keyPair.value().end(); nextLayer++)
952 {
953 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
954 }
955 return;
956 }
957 else if (keyPair.value().type() != nlohmann::json::value_t::string)
James Feist1b2e2242018-01-30 13:45:19 -0800958 {
959 return;
960 }
961
962 std::string value = keyPair.value();
963 if (value.find(TEMPLATE_CHAR) != std::string::npos)
964 {
965 std::string templateValue = value;
966
967 templateValue.erase(0, 1); // remove template character
968
969 // special case index
970 if ("index" == templateValue)
971 {
972 keyPair.value() = foundDeviceIdx;
973 }
974 else
975 {
James Feist13b86d62018-05-29 11:24:35 -0700976 bool found = false;
James Feist1b2e2242018-01-30 13:45:19 -0800977 for (auto &foundDevicePair : foundDevice)
978 {
979 if (boost::iequals(foundDevicePair.first, templateValue))
980 {
James Feist13b86d62018-05-29 11:24:35 -0700981 mapbox::util::apply_visitor(
982 [&](auto &&val) { keyPair.value() = val; },
983 foundDevicePair.second);
984 found = true;
James Feist1b2e2242018-01-30 13:45:19 -0800985 break;
986 }
987 }
James Feist13b86d62018-05-29 11:24:35 -0700988 if (!found)
James Feist1b2e2242018-01-30 13:45:19 -0800989 {
990 std::cerr << "could not find symbol " << templateValue << "\n";
991 }
James Feist1b2e2242018-01-30 13:45:19 -0800992 }
993 }
994}
995
James Feist8f2710a2018-05-09 17:18:55 -0700996// reads json files out of the filesystem
997bool findJsonFiles(std::list<nlohmann::json> &configurations)
James Feist3cb5fec2018-01-23 14:41:51 -0800998{
999 // find configuration files
1000 std::vector<fs::path> jsonPaths;
1001 if (!find_files(fs::path(CONFIGURATION_DIR), R"(.*\.json)", jsonPaths, 0))
1002 {
1003 std::cerr << "Unable to find any configuration files in "
1004 << CONFIGURATION_DIR << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001005 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001006 }
James Feist3cb5fec2018-01-23 14:41:51 -08001007 for (auto &jsonPath : jsonPaths)
1008 {
1009 std::ifstream jsonStream(jsonPath.c_str());
1010 if (!jsonStream.good())
1011 {
1012 std::cerr << "unable to open " << jsonPath.string() << "\n";
1013 continue;
1014 }
1015 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1016 if (data.is_discarded())
1017 {
1018 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1019 continue;
1020 }
1021 if (data.type() == nlohmann::json::value_t::array)
1022 {
1023 for (auto &d : data)
1024 {
1025 configurations.emplace_back(d);
1026 }
1027 }
1028 else
1029 {
1030 configurations.emplace_back(data);
1031 }
1032 }
James Feist75fdeeb2018-02-20 14:26:16 -08001033}
James Feist3cb5fec2018-01-23 14:41:51 -08001034
James Feist8f2710a2018-05-09 17:18:55 -07001035struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001036{
James Feist75fdeeb2018-02-20 14:26:16 -08001037
James Feist8f2710a2018-05-09 17:18:55 -07001038 PerformScan(nlohmann::json &systemConfiguration,
1039 std::list<nlohmann::json> &configurations,
1040 std::function<void(void)> &&callback) :
1041 _systemConfiguration(systemConfiguration),
1042 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001043 {
James Feist8f2710a2018-05-09 17:18:55 -07001044 }
1045 void run()
1046 {
1047 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001048 {
James Feist1b2e2242018-01-30 13:45:19 -08001049 auto findProbe = it->find("probe");
James Feistd63d18a2018-07-19 15:23:45 -07001050 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001051
James Feist1b2e2242018-01-30 13:45:19 -08001052 nlohmann::json probeCommand;
1053 // check for poorly formatted fields, probe must be an array
1054 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001055 {
1056 std::cerr << "configuration file missing probe:\n " << *it
1057 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001058 it = _configurations.erase(it);
1059 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001060 }
James Feist1b2e2242018-01-30 13:45:19 -08001061 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001062 {
1063 probeCommand = nlohmann::json::array();
1064 probeCommand.push_back(*findProbe);
1065 }
1066 else
1067 {
1068 probeCommand = *findProbe;
1069 }
James Feist1b2e2242018-01-30 13:45:19 -08001070
1071 if (findName == it->end())
1072 {
1073 std::cerr << "configuration file missing name:\n " << *it
1074 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001075 it = _configurations.erase(it);
1076 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001077 }
James Feist8f2710a2018-05-09 17:18:55 -07001078 std::string name = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001079
James Feist8f2710a2018-05-09 17:18:55 -07001080 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(), name) !=
1081 PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001082 {
James Feist8f2710a2018-05-09 17:18:55 -07001083 it = _configurations.erase(it);
1084 continue;
1085 }
1086 nlohmann::json *record = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001087
James Feist8f2710a2018-05-09 17:18:55 -07001088 // store reference to this to children to makes sure we don't get
1089 // destroyed too early
1090 auto thisRef = shared_from_this();
1091 auto p = std::make_shared<PerformProbe>(
1092 probeCommand,
1093 [&, record, name,
1094 thisRef](std::vector<boost::container::flat_map<
1095 std::string, BasicVariantType>> &foundDevices) {
1096 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001097
James Feist8f2710a2018-05-09 17:18:55 -07001098 PASSED_PROBES.push_back(name);
1099 size_t foundDeviceIdx = 0;
1100
James Feistbe5425f2018-06-08 10:30:55 -07001101 // insert into configuration temporarly to be able to
1102 // reference ourselves
1103 _systemConfiguration[name] = *record;
1104
James Feist8f2710a2018-05-09 17:18:55 -07001105 for (auto &foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001106 {
James Feist8f2710a2018-05-09 17:18:55 -07001107 for (auto keyPair = record->begin();
1108 keyPair != record->end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001109 {
James Feist1b2e2242018-01-30 13:45:19 -08001110 templateCharReplace(keyPair, foundDevice,
1111 foundDeviceIdx);
James Feist8f2710a2018-05-09 17:18:55 -07001112 }
1113 auto findExpose = record->find("exposes");
1114 if (findExpose == record->end())
1115 {
1116 continue;
1117 }
1118 for (auto &expose : *findExpose)
1119 {
1120 for (auto keyPair = expose.begin();
1121 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001122 {
James Feist1b2e2242018-01-30 13:45:19 -08001123
James Feist8f2710a2018-05-09 17:18:55 -07001124 // fill in template characters with devices
1125 // found
1126 templateCharReplace(keyPair, foundDevice,
1127 foundDeviceIdx);
1128 // special case bind
1129 if (boost::starts_with(keyPair.key(), "bind_"))
1130 {
1131 if (keyPair.value().type() !=
1132 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001133 {
James Feist8f2710a2018-05-09 17:18:55 -07001134 std::cerr << "bind_ value must be of "
1135 "type string "
1136 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001137 continue;
1138 }
James Feist8f2710a2018-05-09 17:18:55 -07001139 bool foundBind = false;
1140 std::string bind = keyPair.key().substr(
1141 sizeof("bind_") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001142
James Feist8f2710a2018-05-09 17:18:55 -07001143 for (auto &configurationPair :
1144 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001145 {
James Feist1b2e2242018-01-30 13:45:19 -08001146
James Feist8f2710a2018-05-09 17:18:55 -07001147 auto configListFind =
1148 configurationPair.value().find(
1149 "exposes");
1150
1151 if (configListFind ==
1152 configurationPair.value()
1153 .end() ||
1154 configListFind->type() !=
1155 nlohmann::json::value_t::array)
1156 {
1157 continue;
1158 }
1159 for (auto &exposedObject :
1160 *configListFind)
1161 {
1162 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001163 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001164 if (boost::iequals(
1165 foundObjectName,
1166 keyPair.value()
1167 .get<std::string>()))
1168 {
1169 exposedObject["status"] =
1170 "okay";
1171 expose[bind] = exposedObject;
1172
1173 foundBind = true;
1174 break;
1175 }
1176 }
1177 if (foundBind)
1178 {
James Feist3cb5fec2018-01-23 14:41:51 -08001179 break;
1180 }
1181 }
James Feist8f2710a2018-05-09 17:18:55 -07001182 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001183 {
James Feist8f2710a2018-05-09 17:18:55 -07001184 std::cerr << "configuration file "
1185 "dependency error, "
1186 "could not find bind "
1187 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001188 }
1189 }
1190 }
1191 }
1192 }
James Feistbe5425f2018-06-08 10:30:55 -07001193 // overwrite ourselves with cleaned up version
James Feist8f2710a2018-05-09 17:18:55 -07001194 _systemConfiguration[name] = *record;
1195 });
1196 p->run();
1197 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001198 }
1199 }
James Feist75fdeeb2018-02-20 14:26:16 -08001200
James Feist8f2710a2018-05-09 17:18:55 -07001201 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001202 {
James Feist8f2710a2018-05-09 17:18:55 -07001203 if (_passed)
1204 {
1205 auto nextScan = std::make_shared<PerformScan>(
1206 _systemConfiguration, _configurations, std::move(_callback));
1207 nextScan->run();
1208 }
1209 else
1210 {
1211 _callback();
1212 }
1213 }
1214 nlohmann::json &_systemConfiguration;
1215 std::list<nlohmann::json> _configurations;
1216 std::function<void(void)> _callback;
1217 std::vector<std::shared_ptr<PerformProbe>> _probes;
1218 bool _passed = false;
1219};
James Feistc95cb142018-02-26 10:41:42 -08001220
James Feist8f2710a2018-05-09 17:18:55 -07001221// main properties changed entry
1222void propertiesChangedCallback(
1223 boost::asio::io_service &io,
1224 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1225 nlohmann::json &systemConfiguration,
1226 sdbusplus::asio::object_server &objServer)
1227{
1228 static boost::asio::deadline_timer timer(io);
1229 timer.expires_from_now(boost::posix_time::seconds(1));
1230
1231 // setup an async wait as we normally get flooded with new requests
1232 timer.async_wait([&](const boost::system::error_code &ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001233 if (ec == boost::asio::error::operation_aborted)
1234 {
1235 // we were cancelled
1236 return;
1237 }
1238 else if (ec)
1239 {
1240 std::cerr << "async wait error " << ec << "\n";
1241 return;
1242 }
1243
1244 nlohmann::json oldConfiguration = systemConfiguration;
1245 DBUS_PROBE_OBJECTS.clear();
1246
1247 std::list<nlohmann::json> configurations;
1248 if (!findJsonFiles(configurations))
1249 {
1250 std::cerr << "cannot find json files\n";
1251 return;
1252 }
1253
1254 auto perfScan = std::make_shared<PerformScan>(
1255 systemConfiguration, configurations, [&, oldConfiguration]() {
1256 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001257 for (auto it = newConfiguration.begin();
1258 it != newConfiguration.end();)
1259 {
1260 auto findKey = oldConfiguration.find(it.key());
1261 if (findKey != oldConfiguration.end())
1262 {
1263 it = newConfiguration.erase(it);
1264 }
1265 else
1266 {
1267 it++;
1268 }
1269 }
James Feist8f2710a2018-05-09 17:18:55 -07001270 registerCallbacks(io, dbusMatches, systemConfiguration,
1271 objServer);
1272 io.post([&, newConfiguration]() {
1273 // todo: for now, only add new configurations,
1274 // unload to come later unloadOverlays();
1275 loadOverlays(newConfiguration);
1276 io.post([&]() { writeJsonFiles(systemConfiguration); });
1277 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001278 postToDbus(newConfiguration, systemConfiguration,
1279 objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001280 });
1281 });
1282 });
1283 perfScan->run();
1284 });
James Feist75fdeeb2018-02-20 14:26:16 -08001285}
1286
James Feist8f2710a2018-05-09 17:18:55 -07001287void registerCallbacks(boost::asio::io_service &io,
1288 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1289 nlohmann::json &systemConfiguration,
1290 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001291{
1292 static boost::container::flat_set<std::string> watchedObjects;
1293
1294 for (const auto &objectMap : DBUS_PROBE_OBJECTS)
1295 {
1296 auto findObject = watchedObjects.find(objectMap.first);
1297 if (findObject != watchedObjects.end())
1298 {
1299 continue;
1300 }
James Feist8f2710a2018-05-09 17:18:55 -07001301 std::function<void(sdbusplus::message::message & message)>
1302 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001303
James Feist8f2710a2018-05-09 17:18:55 -07001304 [&](sdbusplus::message::message &) {
1305 propertiesChangedCallback(io, dbusMatches,
1306 systemConfiguration, objServer);
1307 };
1308
1309 sdbusplus::bus::match::match match(
1310 static_cast<sdbusplus::bus::bus &>(*SYSTEM_BUS),
1311 "type='signal',member='PropertiesChanged',arg0='" +
1312 objectMap.first + "'",
1313 eventHandler);
1314 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001315 }
1316}
1317
1318int main(int argc, char **argv)
1319{
1320 // setup connection to dbus
1321 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001322 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001323 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001324
James Feist8f2710a2018-05-09 17:18:55 -07001325 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001326
James Feist8f2710a2018-05-09 17:18:55 -07001327 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1328 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1329 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001330
James Feist8f2710a2018-05-09 17:18:55 -07001331 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1332 objServer.add_interface("/xyz/openbmc_project/inventory",
1333 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001334
1335 // to keep reference to the match / filter objects so they don't get
1336 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001337 std::vector<sdbusplus::bus::match::match> dbusMatches;
1338
1339 nlohmann::json systemConfiguration = nlohmann::json::object();
1340
1341 inventoryIface->register_method(
1342 "Notify", [](const boost::container::flat_map<
1343 std::string,
1344 boost::container::flat_map<std::string, BasicVariantType>>
1345 &object) { return; });
1346 inventoryIface->initialize();
1347
1348 io.post([&]() {
1349 unloadAllOverlays();
1350 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1351 objServer);
1352 });
James Feist4131aea2018-03-09 09:47:30 -08001353
James Feistfd1264a2018-05-03 12:10:00 -07001354 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001355 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1356 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001357 });
James Feist8f2710a2018-05-09 17:18:55 -07001358 entityIface->initialize();
1359
James Feist1b2e2242018-01-30 13:45:19 -08001360 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001361
1362 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001363}