blob: c46ee7227fe38b14c41995410eef132826b9cc19 [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
James Feistbb43d022018-06-12 15:44:33 -0700505bool 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");
James Feistbb43d022018-06-12 15:44:33 -0700509 if (!output.good())
510 {
511 return false;
512 }
James Feist1b2e2242018-01-30 13:45:19 -0800513 output << systemConfiguration.dump(4);
514 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700515 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700516}
James Feist1b2e2242018-01-30 13:45:19 -0800517
James Feist8f2710a2018-05-09 17:18:55 -0700518// template function to add array as dbus property
519template <typename PropertyType>
520void addArrayToDbus(const std::string &name, const nlohmann::json &array,
James Feistbb43d022018-06-12 15:44:33 -0700521 sdbusplus::asio::dbus_interface *iface,
522 sdbusplus::asio::PropertyPermission permission)
James Feist8f2710a2018-05-09 17:18:55 -0700523{
524 std::vector<PropertyType> values;
525 for (const auto &property : array)
James Feist1b2e2242018-01-30 13:45:19 -0800526 {
James Feist8f2710a2018-05-09 17:18:55 -0700527 auto ptr = property.get_ptr<const PropertyType *>();
528 if (ptr != nullptr)
James Feist1b2e2242018-01-30 13:45:19 -0800529 {
James Feist8f2710a2018-05-09 17:18:55 -0700530 values.emplace_back(*ptr);
James Feist1b2e2242018-01-30 13:45:19 -0800531 }
532 }
James Feistbb43d022018-06-12 15:44:33 -0700533 // todo(james), currently there are no reason to persist arrays, get around
534 // to it if needed
535
536 iface->register_property(name, values, permission);
James Feist1b2e2242018-01-30 13:45:19 -0800537}
James Feist97a63f12018-05-17 13:50:57 -0700538
539template <typename JsonType>
James Feistbb43d022018-06-12 15:44:33 -0700540bool setJsonFromPointer(const std::string &ptrStr, const JsonType &value,
James Feist97a63f12018-05-17 13:50:57 -0700541 nlohmann::json &systemConfiguration)
542{
543 try
544 {
545 nlohmann::json::json_pointer ptr(ptrStr);
546 nlohmann::json &ref = systemConfiguration[ptr];
547 ref = value;
548 return true;
549 }
550 catch (const std::out_of_range)
551 {
552 return false;
553 }
554}
James Feistbb43d022018-06-12 15:44:33 -0700555
556template <typename PropertyType>
557void addProperty(const std::string &propertyName, const PropertyType &value,
558 sdbusplus::asio::dbus_interface *iface,
559 nlohmann::json &systemConfiguration,
560 const std::string &jsonPointerString,
561 sdbusplus::asio::PropertyPermission permission)
562{
563 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
564 {
565 iface->register_property(propertyName, value);
566 return;
567 }
568 iface->register_property(
569 propertyName, value,
570 [&systemConfiguration,
571 jsonPointerString{std::string(jsonPointerString)}](
572 const PropertyType &newVal, PropertyType &val) {
573 val = newVal;
574 if (!setJsonFromPointer(jsonPointerString, val,
575 systemConfiguration))
576 {
577 std::cerr << "error setting json field\n";
578 return -1;
579 }
580 if (writeJsonFiles(systemConfiguration))
581 {
582 std::cerr << "error setting json file\n";
583 return 1;
584 }
585 return -1;
586 });
587}
588
James Feist1b2e2242018-01-30 13:45:19 -0800589// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700590void populateInterfaceFromJson(
591 nlohmann::json &systemConfiguration, const std::string &jsonPointerPath,
592 sdbusplus::asio::dbus_interface *iface, nlohmann::json &dict,
593 sdbusplus::asio::object_server &objServer,
594 sdbusplus::asio::PropertyPermission permission =
595 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800596{
James Feist9eb0b582018-04-27 12:15:46 -0700597 for (auto &dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800598 {
James Feist8f2710a2018-05-09 17:18:55 -0700599 auto type = dictPair.value().type();
600 bool array = false;
601 if (dictPair.value().type() == nlohmann::json::value_t::array)
602 {
603 array = true;
604 if (!dictPair.value().size())
605 {
606 continue;
607 }
608 type = dictPair.value()[0].type();
609 bool isLegal = true;
610 for (const auto &arrayItem : dictPair.value())
611 {
612 if (arrayItem.type() != type)
613 {
614 isLegal = false;
615 break;
616 }
617 }
618 if (!isLegal)
619 {
620 std::cerr << "dbus format error" << dictPair.value() << "\n";
621 continue;
622 }
623 if (type == nlohmann::json::value_t::object)
624 {
625 continue; // handled elsewhere
626 }
627 }
James Feist97a63f12018-05-17 13:50:57 -0700628 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700629 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
630 {
631 // all setable numbers are doubles as it is difficult to always
632 // create a configuration file with all whole numbers as decimals
633 // i.e. 1.0
634 if (dictPair.value().is_number())
635 {
636 type = nlohmann::json::value_t::number_float;
637 }
638 }
639
James Feist8f2710a2018-05-09 17:18:55 -0700640 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800641 {
James Feist9eb0b582018-04-27 12:15:46 -0700642 case (nlohmann::json::value_t::boolean):
643 {
James Feist8f2710a2018-05-09 17:18:55 -0700644 if (array)
645 {
646 // todo: array of bool isn't detected correctly by
647 // sdbusplus, change it to numbers
648 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700649 iface, permission);
James Feist8f2710a2018-05-09 17:18:55 -0700650 }
James Feistbb43d022018-06-12 15:44:33 -0700651
James Feist97a63f12018-05-17 13:50:57 -0700652 else
653 {
James Feistbb43d022018-06-12 15:44:33 -0700654 addProperty(dictPair.key(), dictPair.value().get<bool>(),
655 iface, systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700656 }
James Feist9eb0b582018-04-27 12:15:46 -0700657 break;
658 }
659 case (nlohmann::json::value_t::number_integer):
660 {
James Feist8f2710a2018-05-09 17:18:55 -0700661 if (array)
662 {
663 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700664 iface, permission);
James Feist97a63f12018-05-17 13:50:57 -0700665 }
666 else
667 {
James Feistbb43d022018-06-12 15:44:33 -0700668 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
669 iface, systemConfiguration, key,
670 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700671 }
James Feist9eb0b582018-04-27 12:15:46 -0700672 break;
673 }
674 case (nlohmann::json::value_t::number_unsigned):
675 {
James Feist8f2710a2018-05-09 17:18:55 -0700676 if (array)
677 {
678 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700679 iface, permission);
James Feist97a63f12018-05-17 13:50:57 -0700680 }
681 else
682 {
James Feistbb43d022018-06-12 15:44:33 -0700683 addProperty(dictPair.key(),
684 dictPair.value().get<uint64_t>(), iface,
685 systemConfiguration, key,
686 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700687 }
James Feist9eb0b582018-04-27 12:15:46 -0700688 break;
689 }
690 case (nlohmann::json::value_t::number_float):
691 {
James Feist8f2710a2018-05-09 17:18:55 -0700692 if (array)
693 {
694 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700695 iface, permission);
James Feist8f2710a2018-05-09 17:18:55 -0700696 }
James Feistbb43d022018-06-12 15:44:33 -0700697
James Feist97a63f12018-05-17 13:50:57 -0700698 else
699 {
James Feistbb43d022018-06-12 15:44:33 -0700700 addProperty(dictPair.key(), dictPair.value().get<double>(),
701 iface, systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700702 }
James Feist9eb0b582018-04-27 12:15:46 -0700703 break;
704 }
705 case (nlohmann::json::value_t::string):
706 {
James Feist8f2710a2018-05-09 17:18:55 -0700707 if (array)
708 {
James Feistbb43d022018-06-12 15:44:33 -0700709 addArrayToDbus<std::string>(
710 dictPair.key(), dictPair.value(), iface, permission);
James Feist97a63f12018-05-17 13:50:57 -0700711 }
712 else
713 {
James Feistbb43d022018-06-12 15:44:33 -0700714 addProperty(dictPair.key(),
715 dictPair.value().get<std::string>(), iface,
716 systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700717 }
James Feist9eb0b582018-04-27 12:15:46 -0700718 break;
719 }
James Feist1b2e2242018-01-30 13:45:19 -0800720 }
721 }
James Feist1b2e2242018-01-30 13:45:19 -0800722
James Feist8f2710a2018-05-09 17:18:55 -0700723 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800724}
725
James Feist97a63f12018-05-17 13:50:57 -0700726void postToDbus(const nlohmann::json &newConfiguration,
727 nlohmann::json &systemConfiguration,
James Feist8f2710a2018-05-09 17:18:55 -0700728 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800729
James Feist1b2e2242018-01-30 13:45:19 -0800730{
James Feist97a63f12018-05-17 13:50:57 -0700731 // iterate through boards
732 for (auto &boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800733 {
734 std::string boardKey = boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700735 std::vector<std::string> path;
736 std::string jsonPointerPath = "/" + boardKey;
737 // loop through newConfiguration, but use values from system
738 // configuration to be able to modify via dbus later
739 auto boardValues = systemConfiguration[boardKey];
James Feistd63d18a2018-07-19 15:23:45 -0700740 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800741 std::string boardType;
742 if (findBoardType != boardValues.end() &&
743 findBoardType->type() == nlohmann::json::value_t::string)
744 {
745 boardType = findBoardType->get<std::string>();
746 std::regex_replace(boardType.begin(), boardType.begin(),
747 boardType.end(), ILLEGAL_DBUS_REGEX, "_");
748 }
749 else
750 {
751 std::cerr << "Unable to find type for " << boardKey
752 << " reverting to Chassis.\n";
753 boardType = "Chassis";
754 }
James Feist11be6672018-04-06 14:05:32 -0700755 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800756
757 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
758 ILLEGAL_DBUS_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700759 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
760 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800761
James Feist8f2710a2018-05-09 17:18:55 -0700762 auto inventoryIface = objServer.add_interface(
763 boardName, "xyz.openbmc_project.Inventory.Item");
764 auto boardIface = objServer.add_interface(
765 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700766
James Feist97a63f12018-05-17 13:50:57 -0700767 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
768 boardIface.get(), boardValues, objServer);
769 jsonPointerPath += "/";
770 // iterate through board properties
James Feist9eb0b582018-04-27 12:15:46 -0700771 for (auto &boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700772 {
773 if (boardField.value().type() == nlohmann::json::value_t::object)
774 {
James Feist8f2710a2018-05-09 17:18:55 -0700775 auto iface =
776 objServer.add_interface(boardName, boardField.key());
James Feist97a63f12018-05-17 13:50:57 -0700777 populateInterfaceFromJson(
778 systemConfiguration, jsonPointerPath + boardField.key(),
779 iface.get(), boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700780 }
781 }
James Feist97a63f12018-05-17 13:50:57 -0700782
James Feist1b2e2242018-01-30 13:45:19 -0800783 auto exposes = boardValues.find("exposes");
784 if (exposes == boardValues.end())
785 {
786 continue;
787 }
James Feist97a63f12018-05-17 13:50:57 -0700788 // iterate through exposes
789 jsonPointerPath += "exposes/";
790
791 // store the board level pointer so we can modify it on the way down
792 std::string jsonPointerPathBoard = jsonPointerPath;
793 size_t exposesIndex = -1;
James Feist1b2e2242018-01-30 13:45:19 -0800794 for (auto &item : *exposes)
795 {
James Feist97a63f12018-05-17 13:50:57 -0700796 exposesIndex++;
797 jsonPointerPath = jsonPointerPathBoard;
798 jsonPointerPath += std::to_string(exposesIndex);
799
James Feistd63d18a2018-07-19 15:23:45 -0700800 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800801 if (findName == item.end())
802 {
803 std::cerr << "cannot find name in field " << item << "\n";
804 continue;
805 }
806 auto findStatus = item.find("status");
807 // if status is not found it is assumed to be status = 'okay'
808 if (findStatus != item.end())
809 {
810 if (*findStatus == "disabled")
811 {
812 continue;
813 }
814 }
James Feistd63d18a2018-07-19 15:23:45 -0700815 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800816 std::string itemType;
817 if (findType != item.end())
818 {
819 itemType = findType->get<std::string>();
820 std::regex_replace(itemType.begin(), itemType.begin(),
821 itemType.end(), ILLEGAL_DBUS_REGEX, "_");
822 }
823 else
824 {
825 itemType = "unknown";
826 }
827 std::string itemName = findName->get<std::string>();
828 std::regex_replace(itemName.begin(), itemName.begin(),
829 itemName.end(), ILLEGAL_DBUS_REGEX, "_");
James Feist8f2710a2018-05-09 17:18:55 -0700830 auto itemIface = objServer.add_interface(
831 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800832 "xyz.openbmc_project.Configuration." + itemType);
833
James Feist97a63f12018-05-17 13:50:57 -0700834 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
835 itemIface.get(), item, objServer);
James Feist1b2e2242018-01-30 13:45:19 -0800836
James Feist9eb0b582018-04-27 12:15:46 -0700837 for (auto &objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800838 {
James Feist97a63f12018-05-17 13:50:57 -0700839 jsonPointerPath = jsonPointerPathBoard +
840 std::to_string(exposesIndex) + "/" +
841 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -0800842 if (objectPair.value().type() ==
843 nlohmann::json::value_t::object)
844 {
James Feist8f2710a2018-05-09 17:18:55 -0700845 auto objectIface = objServer.add_interface(
846 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800847 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -0700848 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -0700849
850 populateInterfaceFromJson(
851 systemConfiguration, jsonPointerPath, objectIface.get(),
852 objectPair.value(), objServer);
James Feist1b2e2242018-01-30 13:45:19 -0800853 }
854 else if (objectPair.value().type() ==
855 nlohmann::json::value_t::array)
856 {
857 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -0700858 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -0800859 {
James Feist8f2710a2018-05-09 17:18:55 -0700860 continue;
861 }
862 bool isLegal = true;
863 auto type = objectPair.value()[0].type();
864 if (type != nlohmann::json::value_t::object)
865 {
866 continue;
867 }
868
869 // verify legal json
870 for (const auto &arrayItem : objectPair.value())
871 {
872 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800873 {
James Feist8f2710a2018-05-09 17:18:55 -0700874 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800875 break;
876 }
James Feist8f2710a2018-05-09 17:18:55 -0700877 }
878 if (!isLegal)
879 {
880 std::cerr << "dbus format error" << objectPair.value()
881 << "\n";
882 break;
883 }
884
885 for (auto &arrayItem : objectPair.value())
886 {
James Feistbb43d022018-06-12 15:44:33 -0700887 // limit what interfaces accept set for safety
888 auto permission =
889 std::find(SETTABLE_INTERFACES.begin(),
890 SETTABLE_INTERFACES.end(),
891 objectPair.key()) !=
892 SETTABLE_INTERFACES.end()
893 ? sdbusplus::asio::PropertyPermission::readWrite
894 : sdbusplus::asio::PropertyPermission::readOnly;
James Feist97a63f12018-05-17 13:50:57 -0700895
James Feist8f2710a2018-05-09 17:18:55 -0700896 auto objectIface = objServer.add_interface(
897 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800898 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -0700899 "." + objectPair.key() + std::to_string(index));
900 populateInterfaceFromJson(systemConfiguration,
901 jsonPointerPath + "/" +
902 std::to_string(index),
903 objectIface.get(), arrayItem,
904 objServer, permission);
905 index++;
James Feist1b2e2242018-01-30 13:45:19 -0800906 }
907 }
908 }
909 }
910 }
911}
912
913// finds the template character (currently set to $) and replaces the value with
914// the field found in a dbus object i.e. $ADDRESS would get populated with the
915// ADDRESS field from a object on dbus
916void templateCharReplace(
917 nlohmann::json::iterator &keyPair,
James Feist8f2710a2018-05-09 17:18:55 -0700918 const boost::container::flat_map<std::string, BasicVariantType>
James Feist1b2e2242018-01-30 13:45:19 -0800919 &foundDevice,
920 size_t &foundDeviceIdx)
921{
James Feist11be6672018-04-06 14:05:32 -0700922 if (keyPair.value().type() == nlohmann::json::value_t::object)
923 {
924 for (auto nextLayer = keyPair.value().begin();
925 nextLayer != keyPair.value().end(); nextLayer++)
926 {
927 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
928 }
929 return;
930 }
931 else if (keyPair.value().type() != nlohmann::json::value_t::string)
James Feist1b2e2242018-01-30 13:45:19 -0800932 {
933 return;
934 }
935
936 std::string value = keyPair.value();
937 if (value.find(TEMPLATE_CHAR) != std::string::npos)
938 {
939 std::string templateValue = value;
940
941 templateValue.erase(0, 1); // remove template character
942
943 // special case index
944 if ("index" == templateValue)
945 {
946 keyPair.value() = foundDeviceIdx;
947 }
948 else
949 {
James Feist13b86d62018-05-29 11:24:35 -0700950 bool found = false;
James Feist1b2e2242018-01-30 13:45:19 -0800951 for (auto &foundDevicePair : foundDevice)
952 {
953 if (boost::iequals(foundDevicePair.first, templateValue))
954 {
James Feist13b86d62018-05-29 11:24:35 -0700955 mapbox::util::apply_visitor(
956 [&](auto &&val) { keyPair.value() = val; },
957 foundDevicePair.second);
958 found = true;
James Feist1b2e2242018-01-30 13:45:19 -0800959 break;
960 }
961 }
James Feist13b86d62018-05-29 11:24:35 -0700962 if (!found)
James Feist1b2e2242018-01-30 13:45:19 -0800963 {
964 std::cerr << "could not find symbol " << templateValue << "\n";
965 }
James Feist1b2e2242018-01-30 13:45:19 -0800966 }
967 }
968}
969
James Feist8f2710a2018-05-09 17:18:55 -0700970// reads json files out of the filesystem
971bool findJsonFiles(std::list<nlohmann::json> &configurations)
James Feist3cb5fec2018-01-23 14:41:51 -0800972{
973 // find configuration files
974 std::vector<fs::path> jsonPaths;
975 if (!find_files(fs::path(CONFIGURATION_DIR), R"(.*\.json)", jsonPaths, 0))
976 {
977 std::cerr << "Unable to find any configuration files in "
978 << CONFIGURATION_DIR << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -0800979 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800980 }
James Feist3cb5fec2018-01-23 14:41:51 -0800981 for (auto &jsonPath : jsonPaths)
982 {
983 std::ifstream jsonStream(jsonPath.c_str());
984 if (!jsonStream.good())
985 {
986 std::cerr << "unable to open " << jsonPath.string() << "\n";
987 continue;
988 }
989 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
990 if (data.is_discarded())
991 {
992 std::cerr << "syntax error in " << jsonPath.string() << "\n";
993 continue;
994 }
995 if (data.type() == nlohmann::json::value_t::array)
996 {
997 for (auto &d : data)
998 {
999 configurations.emplace_back(d);
1000 }
1001 }
1002 else
1003 {
1004 configurations.emplace_back(data);
1005 }
1006 }
James Feist75fdeeb2018-02-20 14:26:16 -08001007}
James Feist3cb5fec2018-01-23 14:41:51 -08001008
James Feist8f2710a2018-05-09 17:18:55 -07001009struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001010{
James Feist75fdeeb2018-02-20 14:26:16 -08001011
James Feist8f2710a2018-05-09 17:18:55 -07001012 PerformScan(nlohmann::json &systemConfiguration,
1013 std::list<nlohmann::json> &configurations,
1014 std::function<void(void)> &&callback) :
1015 _systemConfiguration(systemConfiguration),
1016 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001017 {
James Feist8f2710a2018-05-09 17:18:55 -07001018 }
1019 void run()
1020 {
1021 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001022 {
James Feist1b2e2242018-01-30 13:45:19 -08001023 auto findProbe = it->find("probe");
James Feistd63d18a2018-07-19 15:23:45 -07001024 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001025
James Feist1b2e2242018-01-30 13:45:19 -08001026 nlohmann::json probeCommand;
1027 // check for poorly formatted fields, probe must be an array
1028 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001029 {
1030 std::cerr << "configuration file missing probe:\n " << *it
1031 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001032 it = _configurations.erase(it);
1033 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001034 }
James Feist1b2e2242018-01-30 13:45:19 -08001035 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001036 {
1037 probeCommand = nlohmann::json::array();
1038 probeCommand.push_back(*findProbe);
1039 }
1040 else
1041 {
1042 probeCommand = *findProbe;
1043 }
James Feist1b2e2242018-01-30 13:45:19 -08001044
1045 if (findName == it->end())
1046 {
1047 std::cerr << "configuration file missing name:\n " << *it
1048 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001049 it = _configurations.erase(it);
1050 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001051 }
James Feist8f2710a2018-05-09 17:18:55 -07001052 std::string name = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001053
James Feist8f2710a2018-05-09 17:18:55 -07001054 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(), name) !=
1055 PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001056 {
James Feist8f2710a2018-05-09 17:18:55 -07001057 it = _configurations.erase(it);
1058 continue;
1059 }
1060 nlohmann::json *record = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001061
James Feist8f2710a2018-05-09 17:18:55 -07001062 // store reference to this to children to makes sure we don't get
1063 // destroyed too early
1064 auto thisRef = shared_from_this();
1065 auto p = std::make_shared<PerformProbe>(
1066 probeCommand,
1067 [&, record, name,
1068 thisRef](std::vector<boost::container::flat_map<
1069 std::string, BasicVariantType>> &foundDevices) {
1070 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001071
James Feist8f2710a2018-05-09 17:18:55 -07001072 PASSED_PROBES.push_back(name);
1073 size_t foundDeviceIdx = 0;
1074
James Feistbe5425f2018-06-08 10:30:55 -07001075 // insert into configuration temporarly to be able to
1076 // reference ourselves
1077 _systemConfiguration[name] = *record;
1078
James Feist8f2710a2018-05-09 17:18:55 -07001079 for (auto &foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001080 {
James Feist8f2710a2018-05-09 17:18:55 -07001081 for (auto keyPair = record->begin();
1082 keyPair != record->end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001083 {
James Feist1b2e2242018-01-30 13:45:19 -08001084 templateCharReplace(keyPair, foundDevice,
1085 foundDeviceIdx);
James Feist8f2710a2018-05-09 17:18:55 -07001086 }
1087 auto findExpose = record->find("exposes");
1088 if (findExpose == record->end())
1089 {
1090 continue;
1091 }
1092 for (auto &expose : *findExpose)
1093 {
1094 for (auto keyPair = expose.begin();
1095 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001096 {
James Feist1b2e2242018-01-30 13:45:19 -08001097
James Feist8f2710a2018-05-09 17:18:55 -07001098 // fill in template characters with devices
1099 // found
1100 templateCharReplace(keyPair, foundDevice,
1101 foundDeviceIdx);
1102 // special case bind
1103 if (boost::starts_with(keyPair.key(), "bind_"))
1104 {
1105 if (keyPair.value().type() !=
1106 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001107 {
James Feist8f2710a2018-05-09 17:18:55 -07001108 std::cerr << "bind_ value must be of "
1109 "type string "
1110 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001111 continue;
1112 }
James Feist8f2710a2018-05-09 17:18:55 -07001113 bool foundBind = false;
1114 std::string bind = keyPair.key().substr(
1115 sizeof("bind_") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001116
James Feist8f2710a2018-05-09 17:18:55 -07001117 for (auto &configurationPair :
1118 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001119 {
James Feist1b2e2242018-01-30 13:45:19 -08001120
James Feist8f2710a2018-05-09 17:18:55 -07001121 auto configListFind =
1122 configurationPair.value().find(
1123 "exposes");
1124
1125 if (configListFind ==
1126 configurationPair.value()
1127 .end() ||
1128 configListFind->type() !=
1129 nlohmann::json::value_t::array)
1130 {
1131 continue;
1132 }
1133 for (auto &exposedObject :
1134 *configListFind)
1135 {
1136 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001137 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001138 if (boost::iequals(
1139 foundObjectName,
1140 keyPair.value()
1141 .get<std::string>()))
1142 {
1143 exposedObject["status"] =
1144 "okay";
1145 expose[bind] = exposedObject;
1146
1147 foundBind = true;
1148 break;
1149 }
1150 }
1151 if (foundBind)
1152 {
James Feist3cb5fec2018-01-23 14:41:51 -08001153 break;
1154 }
1155 }
James Feist8f2710a2018-05-09 17:18:55 -07001156 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001157 {
James Feist8f2710a2018-05-09 17:18:55 -07001158 std::cerr << "configuration file "
1159 "dependency error, "
1160 "could not find bind "
1161 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001162 }
1163 }
1164 }
1165 }
1166 }
James Feistbe5425f2018-06-08 10:30:55 -07001167 // overwrite ourselves with cleaned up version
James Feist8f2710a2018-05-09 17:18:55 -07001168 _systemConfiguration[name] = *record;
1169 });
1170 p->run();
1171 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001172 }
1173 }
James Feist75fdeeb2018-02-20 14:26:16 -08001174
James Feist8f2710a2018-05-09 17:18:55 -07001175 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001176 {
James Feist8f2710a2018-05-09 17:18:55 -07001177 if (_passed)
1178 {
1179 auto nextScan = std::make_shared<PerformScan>(
1180 _systemConfiguration, _configurations, std::move(_callback));
1181 nextScan->run();
1182 }
1183 else
1184 {
1185 _callback();
1186 }
1187 }
1188 nlohmann::json &_systemConfiguration;
1189 std::list<nlohmann::json> _configurations;
1190 std::function<void(void)> _callback;
1191 std::vector<std::shared_ptr<PerformProbe>> _probes;
1192 bool _passed = false;
1193};
James Feistc95cb142018-02-26 10:41:42 -08001194
James Feist8f2710a2018-05-09 17:18:55 -07001195// main properties changed entry
1196void propertiesChangedCallback(
1197 boost::asio::io_service &io,
1198 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1199 nlohmann::json &systemConfiguration,
1200 sdbusplus::asio::object_server &objServer)
1201{
1202 static boost::asio::deadline_timer timer(io);
1203 timer.expires_from_now(boost::posix_time::seconds(1));
1204
1205 // setup an async wait as we normally get flooded with new requests
1206 timer.async_wait([&](const boost::system::error_code &ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001207 if (ec == boost::asio::error::operation_aborted)
1208 {
1209 // we were cancelled
1210 return;
1211 }
1212 else if (ec)
1213 {
1214 std::cerr << "async wait error " << ec << "\n";
1215 return;
1216 }
1217
1218 nlohmann::json oldConfiguration = systemConfiguration;
1219 DBUS_PROBE_OBJECTS.clear();
1220
1221 std::list<nlohmann::json> configurations;
1222 if (!findJsonFiles(configurations))
1223 {
1224 std::cerr << "cannot find json files\n";
1225 return;
1226 }
1227
1228 auto perfScan = std::make_shared<PerformScan>(
1229 systemConfiguration, configurations, [&, oldConfiguration]() {
1230 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001231 for (auto it = newConfiguration.begin();
1232 it != newConfiguration.end();)
1233 {
1234 auto findKey = oldConfiguration.find(it.key());
1235 if (findKey != oldConfiguration.end())
1236 {
1237 it = newConfiguration.erase(it);
1238 }
1239 else
1240 {
1241 it++;
1242 }
1243 }
James Feist8f2710a2018-05-09 17:18:55 -07001244 registerCallbacks(io, dbusMatches, systemConfiguration,
1245 objServer);
1246 io.post([&, newConfiguration]() {
1247 // todo: for now, only add new configurations,
1248 // unload to come later unloadOverlays();
1249 loadOverlays(newConfiguration);
James Feistbb43d022018-06-12 15:44:33 -07001250 io.post([&]() {
1251 if (!writeJsonFiles(systemConfiguration))
1252 {
1253 std::cerr << "Error writing json files\n";
1254 }
1255 });
James Feist8f2710a2018-05-09 17:18:55 -07001256 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001257 postToDbus(newConfiguration, systemConfiguration,
1258 objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001259 });
1260 });
1261 });
1262 perfScan->run();
1263 });
James Feist75fdeeb2018-02-20 14:26:16 -08001264}
1265
James Feist8f2710a2018-05-09 17:18:55 -07001266void registerCallbacks(boost::asio::io_service &io,
1267 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1268 nlohmann::json &systemConfiguration,
1269 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001270{
1271 static boost::container::flat_set<std::string> watchedObjects;
1272
1273 for (const auto &objectMap : DBUS_PROBE_OBJECTS)
1274 {
1275 auto findObject = watchedObjects.find(objectMap.first);
1276 if (findObject != watchedObjects.end())
1277 {
1278 continue;
1279 }
James Feist8f2710a2018-05-09 17:18:55 -07001280 std::function<void(sdbusplus::message::message & message)>
1281 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001282
James Feist8f2710a2018-05-09 17:18:55 -07001283 [&](sdbusplus::message::message &) {
1284 propertiesChangedCallback(io, dbusMatches,
1285 systemConfiguration, objServer);
1286 };
1287
1288 sdbusplus::bus::match::match match(
1289 static_cast<sdbusplus::bus::bus &>(*SYSTEM_BUS),
1290 "type='signal',member='PropertiesChanged',arg0='" +
1291 objectMap.first + "'",
1292 eventHandler);
1293 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001294 }
1295}
1296
1297int main(int argc, char **argv)
1298{
1299 // setup connection to dbus
1300 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001301 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001302 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001303
James Feist8f2710a2018-05-09 17:18:55 -07001304 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001305
James Feist8f2710a2018-05-09 17:18:55 -07001306 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1307 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1308 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001309
James Feist8f2710a2018-05-09 17:18:55 -07001310 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1311 objServer.add_interface("/xyz/openbmc_project/inventory",
1312 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001313
1314 // to keep reference to the match / filter objects so they don't get
1315 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001316 std::vector<sdbusplus::bus::match::match> dbusMatches;
1317
1318 nlohmann::json systemConfiguration = nlohmann::json::object();
1319
1320 inventoryIface->register_method(
1321 "Notify", [](const boost::container::flat_map<
1322 std::string,
1323 boost::container::flat_map<std::string, BasicVariantType>>
1324 &object) { return; });
1325 inventoryIface->initialize();
1326
1327 io.post([&]() {
1328 unloadAllOverlays();
1329 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1330 objServer);
1331 });
James Feist4131aea2018-03-09 09:47:30 -08001332
James Feistfd1264a2018-05-03 12:10:00 -07001333 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001334 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1335 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001336 });
James Feist8f2710a2018-05-09 17:18:55 -07001337 entityIface->initialize();
1338
James Feist1b2e2242018-01-30 13:45:19 -08001339 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001340
1341 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001342}