blob: e490420b86e090fd752c44e1eb70acca54168f2b [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 Feist8f2710a2018-05-09 17:18:55 -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 {
150 return;
151 }
James Feist494155a2018-03-14 16:23:24 -0700152 }
James Feist8f2710a2018-05-09 17:18:55 -0700153 else
James Feist3cb5fec2018-01-23 14:41:51 -0800154 {
James Feist8f2710a2018-05-09 17:18:55 -0700155 for (auto &object : interfaceSubtree)
156 {
157 for (auto &connPair : object.second)
158 {
159 auto insertPair =
160 interfaceConnections.insert(connPair.first);
161 }
162 }
James Feist3cb5fec2018-01-23 14:41:51 -0800163 }
James Feist8f2710a2018-05-09 17:18:55 -0700164 // get managed objects for all interfaces
165 for (const auto &conn : interfaceConnections)
166 {
167 connection->async_method_call(
168 [&, conn,
169 interface](boost::system::error_code &ec,
170 const ManagedObjectType &managedInterface) {
171 if (ec)
172 {
173 std::cerr
174 << "error getting managed object for device "
175 << conn << "\n";
176 pendingProbes[interface].clear();
177 return;
178 }
179 for (auto &interfaceManagedObj : managedInterface)
180 {
181 auto ifaceObjFind =
182 interfaceManagedObj.second.find(interface);
183 if (ifaceObjFind !=
184 interfaceManagedObj.second.end())
185 {
186 std::vector<boost::container::flat_map<
187 std::string, BasicVariantType>>
188 &dbusObject = DBUS_PROBE_OBJECTS[interface];
189 dbusObject.emplace_back(ifaceObjFind->second);
190 }
191 }
192 pendingProbes[interface].clear();
193 },
194 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
195 "GetManagedObjects");
196 }
197 },
198 "xyz.openbmc_project.ObjectMapper",
199 "/xyz/openbmc_project/object_mapper",
200 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "", MAX_MAPPER_DEPTH,
201 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800202}
James Feist8f2710a2018-05-09 17:18:55 -0700203// probes dbus interface dictionary for a key with a value that matches a regex
James Feist3cb5fec2018-01-23 14:41:51 -0800204bool probeDbus(
205 const std::string &interface,
206 const std::map<std::string, nlohmann::json> &matches,
James Feist8f2710a2018-05-09 17:18:55 -0700207 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
James Feist3cb5fec2018-01-23 14:41:51 -0800208 &devices,
209 bool &foundProbe)
210{
James Feist8f2710a2018-05-09 17:18:55 -0700211 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
212 &dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800213 if (dbusObject.empty())
214 {
James Feist8f2710a2018-05-09 17:18:55 -0700215 foundProbe = false;
216 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800217 }
218 foundProbe = true;
219
220 bool foundMatch = false;
221 for (auto &device : dbusObject)
222 {
223 bool deviceMatches = true;
224 for (auto &match : matches)
225 {
226 auto deviceValue = device.find(match.first);
227 if (deviceValue != device.end())
228 {
229 switch (match.second.type())
230 {
James Feist9eb0b582018-04-27 12:15:46 -0700231 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800232 {
James Feist9eb0b582018-04-27 12:15:46 -0700233 std::regex search(match.second.get<std::string>());
234 std::smatch match;
235
236 // convert value to string respresentation
James Feist8f2710a2018-05-09 17:18:55 -0700237 std::string probeValue = mapbox::util::apply_visitor(
238 VariantToStringVisitor(), deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700239 if (!std::regex_search(probeValue, match, search))
240 {
241 deviceMatches = false;
242 break;
243 }
James Feist3cb5fec2018-01-23 14:41:51 -0800244 break;
245 }
James Feist9eb0b582018-04-27 12:15:46 -0700246 case nlohmann::json::value_t::boolean:
247 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800248 {
James Feist8f2710a2018-05-09 17:18:55 -0700249 unsigned int probeValue = mapbox::util::apply_visitor(
James Feist9eb0b582018-04-27 12:15:46 -0700250 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800251
James Feist9eb0b582018-04-27 12:15:46 -0700252 if (probeValue != match.second.get<unsigned int>())
253 {
254 deviceMatches = false;
255 }
256 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800257 }
James Feist9eb0b582018-04-27 12:15:46 -0700258 case nlohmann::json::value_t::number_integer:
259 {
James Feist8f2710a2018-05-09 17:18:55 -0700260 int probeValue = mapbox::util::apply_visitor(
James Feist9eb0b582018-04-27 12:15:46 -0700261 VariantToIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800262
James Feist9eb0b582018-04-27 12:15:46 -0700263 if (probeValue != match.second.get<int>())
264 {
265 deviceMatches = false;
266 }
267 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800268 }
James Feist9eb0b582018-04-27 12:15:46 -0700269 case nlohmann::json::value_t::number_float:
270 {
James Feist8f2710a2018-05-09 17:18:55 -0700271 float probeValue = mapbox::util::apply_visitor(
James Feist9eb0b582018-04-27 12:15:46 -0700272 VariantToFloatVisitor(), deviceValue->second);
273
274 if (probeValue != match.second.get<float>())
275 {
276 deviceMatches = false;
277 }
278 break;
279 }
James Feist3cb5fec2018-01-23 14:41:51 -0800280 }
281 }
282 else
283 {
284 deviceMatches = false;
285 break;
286 }
287 }
288 if (deviceMatches)
289 {
290 devices.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700291 boost::container::flat_map<std::string, BasicVariantType>(
James Feist3cb5fec2018-01-23 14:41:51 -0800292 device));
293 foundMatch = true;
294 deviceMatches = false; // for next iteration
295 }
296 }
297 return foundMatch;
298}
299
300// default probe entry point, iterates a list looking for specific types to
301// call specific probe functions
302bool probe(
James Feist8f2710a2018-05-09 17:18:55 -0700303 const std::vector<std::string> &probeCommand,
304 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
James Feist3cb5fec2018-01-23 14:41:51 -0800305 &foundDevs)
306{
307 const static std::regex command(R"(\((.*)\))");
308 std::smatch match;
309 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700310 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800311 bool cur = true;
312 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
313
314 for (auto &probe : probeCommand)
315 {
316 bool foundProbe = false;
317 boost::container::flat_map<const char *, probe_type_codes,
318 cmp_str>::const_iterator probeType;
319
320 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
321 probeType++)
322 {
323 if (probe.find(probeType->first) != std::string::npos)
324 {
325 foundProbe = true;
326 break;
327 }
328 }
329 if (foundProbe)
330 {
331 switch (probeType->second)
332 {
James Feist9eb0b582018-04-27 12:15:46 -0700333 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800334 {
James Feist8f2710a2018-05-09 17:18:55 -0700335 return false;
James Feist9eb0b582018-04-27 12:15:46 -0700336 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800337 }
James Feist9eb0b582018-04-27 12:15:46 -0700338 case probe_type_codes::TRUE_T:
339 {
James Feist8f2710a2018-05-09 17:18:55 -0700340 return true;
James Feist9eb0b582018-04-27 12:15:46 -0700341 break;
342 }
343 case probe_type_codes::MATCH_ONE:
344 {
345 // set current value to last, this probe type shouldn't
346 // affect the outcome
347 cur = ret;
348 matchOne = true;
349 break;
350 }
351 /*case probe_type_codes::AND:
352 break;
353 case probe_type_codes::OR:
354 break;
355 // these are no-ops until the last command switch
356 */
357 case probe_type_codes::FOUND:
358 {
359 if (!std::regex_search(probe, match, command))
360 {
361 std::cerr << "found probe sytax error " << probe
362 << "\n";
363 return false;
364 }
365 std::string commandStr = *(match.begin() + 1);
366 boost::replace_all(commandStr, "'", "");
367 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
368 commandStr) != PASSED_PROBES.end());
369 break;
370 }
James Feist3cb5fec2018-01-23 14:41:51 -0800371 }
372 }
373 // look on dbus for object
374 else
375 {
376 if (!std::regex_search(probe, match, command))
377 {
378 std::cerr << "dbus probe sytax error " << probe << "\n";
379 return false;
380 }
381 std::string commandStr = *(match.begin() + 1);
382 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700383 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800384 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800385 auto json = nlohmann::json::parse(commandStr, nullptr, false);
386 if (json.is_discarded())
387 {
388 std::cerr << "dbus command sytax error " << commandStr << "\n";
389 return false;
390 }
391 // we can match any (string, variant) property. (string, string)
392 // does a regex
393 std::map<std::string, nlohmann::json> dbusProbeMap =
394 json.get<std::map<std::string, nlohmann::json>>();
395 auto findStart = probe.find("(");
396 if (findStart == std::string::npos)
397 {
398 return false;
399 }
400 std::string probeInterface = probe.substr(0, findStart);
401 cur =
402 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
403 }
404
405 // some functions like AND and OR only take affect after the
406 // fact
407 switch (lastCommand)
408 {
James Feist9eb0b582018-04-27 12:15:46 -0700409 case probe_type_codes::AND:
410 ret = cur && ret;
411 break;
412 case probe_type_codes::OR:
413 ret = cur || ret;
414 break;
415 default:
416 ret = cur;
417 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800418 }
419 lastCommand = probeType != PROBE_TYPES.end()
420 ? probeType->second
421 : probe_type_codes::FALSE_T;
422
423 if (!foundProbe)
424 {
425 std::cerr << "Illegal probe type " << probe << "\n";
426 return false;
427 }
428 }
429
430 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800431 if (ret && foundDevs.size() == 0)
432 {
433 foundDevs.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700434 boost::container::flat_map<std::string, BasicVariantType>());
James Feist3cb5fec2018-01-23 14:41:51 -0800435 }
James Feist6bd2a022018-03-13 12:30:58 -0700436 if (matchOne && foundDevs.size() > 1)
437 {
438 foundDevs.erase(foundDevs.begin() + 1, foundDevs.end());
439 }
James Feist3cb5fec2018-01-23 14:41:51 -0800440 return ret;
441}
James Feist8f2710a2018-05-09 17:18:55 -0700442// this class finds the needed dbus fields and on destruction runs the probe
443struct PerformProbe : std::enable_shared_from_this<PerformProbe>
444{
James Feist3cb5fec2018-01-23 14:41:51 -0800445
James Feist8f2710a2018-05-09 17:18:55 -0700446 PerformProbe(
447 const std::vector<std::string> &probeCommand,
448 std::function<void(std::vector<boost::container::flat_map<
449 std::string, BasicVariantType>> &)> &&callback) :
450 _probeCommand(probeCommand),
451 _callback(std::move(callback))
452 {
453 }
454 ~PerformProbe()
455 {
456 if (probe(_probeCommand, _foundDevs))
457 {
458 _callback(_foundDevs);
459 }
460 }
461 void run()
462 {
463 // parse out dbus probes by discarding other probe types
464 boost::container::flat_map<const char *, probe_type_codes,
465 cmp_str>::const_iterator probeType;
466
467 std::vector<std::string> dbusProbes;
468 for (std::string &probe : _probeCommand)
469 {
470 bool found = false;
471 boost::container::flat_map<const char *, probe_type_codes,
472 cmp_str>::const_iterator probeType;
473 for (probeType = PROBE_TYPES.begin();
474 probeType != PROBE_TYPES.end(); probeType++)
475 {
476 if (probe.find(probeType->first) != std::string::npos)
477 {
478 found = true;
479 break;
480 }
481 }
482 if (found)
483 {
484 continue;
485 }
486 // syntax requires probe before first open brace
487 auto findStart = probe.find("(");
488 std::string interface = probe.substr(0, findStart);
489
490 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
491 }
492 }
493 std::vector<std::string> _probeCommand;
494 std::function<void(
495 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
496 &)>
497 _callback;
498 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
499 _foundDevs;
500};
501
502// writes output files to persist data
503void writeJsonFiles(const nlohmann::json &systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800504{
505 std::experimental::filesystem::create_directory(OUTPUT_DIR);
506 std::ofstream output(std::string(OUTPUT_DIR) + "system.json");
507 output << systemConfiguration.dump(4);
508 output.close();
James Feist8f2710a2018-05-09 17:18:55 -0700509}
James Feist1b2e2242018-01-30 13:45:19 -0800510
James Feist8f2710a2018-05-09 17:18:55 -0700511// template function to add array as dbus property
512template <typename PropertyType>
513void addArrayToDbus(const std::string &name, const nlohmann::json &array,
514 sdbusplus::asio::dbus_interface *iface)
515{
516 std::vector<PropertyType> values;
517 for (const auto &property : array)
James Feist1b2e2242018-01-30 13:45:19 -0800518 {
James Feist8f2710a2018-05-09 17:18:55 -0700519 auto ptr = property.get_ptr<const PropertyType *>();
520 if (ptr != nullptr)
James Feist1b2e2242018-01-30 13:45:19 -0800521 {
James Feist8f2710a2018-05-09 17:18:55 -0700522 values.emplace_back(*ptr);
James Feist1b2e2242018-01-30 13:45:19 -0800523 }
524 }
James Feist8f2710a2018-05-09 17:18:55 -0700525 iface->register_property(name, values);
James Feist1b2e2242018-01-30 13:45:19 -0800526}
James Feist97a63f12018-05-17 13:50:57 -0700527
528template <typename JsonType>
529bool SetJsonFromPointer(const std::string &ptrStr, const JsonType &value,
530 nlohmann::json &systemConfiguration)
531{
532 try
533 {
534 nlohmann::json::json_pointer ptr(ptrStr);
535 nlohmann::json &ref = systemConfiguration[ptr];
536 ref = value;
537 return true;
538 }
539 catch (const std::out_of_range)
540 {
541 return false;
542 }
543}
James Feist1b2e2242018-01-30 13:45:19 -0800544// adds simple json types to interface's properties
James Feist97a63f12018-05-17 13:50:57 -0700545void populateInterfaceFromJson(nlohmann::json &systemConfiguration,
546 const std::string &jsonPointerPath,
James Feist8f2710a2018-05-09 17:18:55 -0700547 sdbusplus::asio::dbus_interface *iface,
548 nlohmann::json &dict,
James Feist97a63f12018-05-17 13:50:57 -0700549 sdbusplus::asio::object_server &objServer,
550 bool setable = false)
James Feist1b2e2242018-01-30 13:45:19 -0800551{
James Feist9eb0b582018-04-27 12:15:46 -0700552 for (auto &dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800553 {
James Feist8f2710a2018-05-09 17:18:55 -0700554 auto type = dictPair.value().type();
555 bool array = false;
556 if (dictPair.value().type() == nlohmann::json::value_t::array)
557 {
558 array = true;
559 if (!dictPair.value().size())
560 {
561 continue;
562 }
563 type = dictPair.value()[0].type();
564 bool isLegal = true;
565 for (const auto &arrayItem : dictPair.value())
566 {
567 if (arrayItem.type() != type)
568 {
569 isLegal = false;
570 break;
571 }
572 }
573 if (!isLegal)
574 {
575 std::cerr << "dbus format error" << dictPair.value() << "\n";
576 continue;
577 }
578 if (type == nlohmann::json::value_t::object)
579 {
580 continue; // handled elsewhere
581 }
582 }
James Feist97a63f12018-05-17 13:50:57 -0700583 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feist8f2710a2018-05-09 17:18:55 -0700584 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800585 {
James Feist9eb0b582018-04-27 12:15:46 -0700586 case (nlohmann::json::value_t::boolean):
587 {
James Feist8f2710a2018-05-09 17:18:55 -0700588 if (array)
589 {
590 // todo: array of bool isn't detected correctly by
591 // sdbusplus, change it to numbers
592 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
593 iface);
594 break;
595 }
James Feist97a63f12018-05-17 13:50:57 -0700596 if (setable)
597 {
598 iface->register_property(
599 std::string(dictPair.key()),
600 dictPair.value().get<bool>(),
601 [&, key](const bool &newVal, bool &val) {
602 val = newVal;
603 if (!SetJsonFromPointer(key, val,
604 systemConfiguration))
605 {
606 std::cerr << "error writing json\n";
607 return -1;
608 }
609 writeJsonFiles(systemConfiguration);
610 return 1;
611 });
612 }
613 else
614 {
615 iface->register_property(std::string(dictPair.key()),
616 dictPair.value().get<bool>());
617 }
James Feist9eb0b582018-04-27 12:15:46 -0700618 break;
619 }
620 case (nlohmann::json::value_t::number_integer):
621 {
James Feist8f2710a2018-05-09 17:18:55 -0700622 if (array)
623 {
624 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
625 iface);
626 break;
627 }
James Feist97a63f12018-05-17 13:50:57 -0700628 if (setable)
629 {
630 iface->register_property(
631 std::string(dictPair.key()),
632 dictPair.value().get<int64_t>(),
633 [&, key](const int64_t &newVal, int64_t &val) {
634 val = newVal;
635 if (!SetJsonFromPointer(key, val,
636 systemConfiguration))
637 {
638 std::cerr << "error writing json\n";
639 return -1;
640 }
641 writeJsonFiles(systemConfiguration);
642 return 1;
643 });
644 }
645 else
646 {
647 iface->register_property(std::string(dictPair.key()),
648 dictPair.value().get<int64_t>());
649 }
James Feist9eb0b582018-04-27 12:15:46 -0700650 break;
651 }
652 case (nlohmann::json::value_t::number_unsigned):
653 {
James Feist8f2710a2018-05-09 17:18:55 -0700654 if (array)
655 {
656 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
657 iface);
658 break;
659 }
James Feist97a63f12018-05-17 13:50:57 -0700660 if (setable)
661 {
662 iface->register_property(
663 std::string(dictPair.key()),
664 dictPair.value().get<uint64_t>(),
665 [&, key](const uint64_t &newVal, uint64_t &val) {
666 val = newVal;
667 if (!SetJsonFromPointer(key, val,
668 systemConfiguration))
669 {
670 std::cerr << "error writing json\n";
671 return -1;
672 }
673 writeJsonFiles(systemConfiguration);
674 return 1;
675 });
676 }
677 else
678 {
679 iface->register_property(std::string(dictPair.key()),
680 dictPair.value().get<uint64_t>());
681 }
James Feist9eb0b582018-04-27 12:15:46 -0700682 break;
683 }
684 case (nlohmann::json::value_t::number_float):
685 {
James Feist8f2710a2018-05-09 17:18:55 -0700686 if (array)
687 {
688 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
689 iface);
690 break;
691 }
James Feist97a63f12018-05-17 13:50:57 -0700692 if (setable)
693 {
694 iface->register_property(
695 std::string(dictPair.key()),
696 dictPair.value().get<double>(),
697 [&, key](const double &newVal, double &val) {
698 val = newVal;
699 if (!SetJsonFromPointer(key, val,
700 systemConfiguration))
701 {
702 std::cerr << "error writing json\n";
703 return -1;
704 }
705 return 1;
706 });
707 }
708 else
709 {
710 iface->register_property(std::string(dictPair.key()),
711 dictPair.value().get<double>());
712 }
James Feist9eb0b582018-04-27 12:15:46 -0700713 break;
714 }
715 case (nlohmann::json::value_t::string):
716 {
James Feist8f2710a2018-05-09 17:18:55 -0700717 if (array)
718 {
719 addArrayToDbus<std::string>(dictPair.key(),
720 dictPair.value(), iface);
721 break;
722 }
James Feist97a63f12018-05-17 13:50:57 -0700723 if (setable)
724 {
725 iface->register_property(
726 std::string(dictPair.key()),
727 dictPair.value().get<std::string>(),
728 [&, key](const std::string &newVal, std::string &val) {
729 val = newVal;
730 if (!SetJsonFromPointer(key, val,
731 systemConfiguration))
732 {
733 std::cerr << "error writing json\n";
734 return -1;
735 }
736 writeJsonFiles(systemConfiguration);
737 return 1;
738 });
739 }
740 else
741 {
742 iface->register_property(
743 std::string(dictPair.key()),
744 dictPair.value().get<std::string>());
745 }
James Feist9eb0b582018-04-27 12:15:46 -0700746 break;
747 }
James Feist1b2e2242018-01-30 13:45:19 -0800748 }
749 }
James Feist1b2e2242018-01-30 13:45:19 -0800750
James Feist8f2710a2018-05-09 17:18:55 -0700751 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800752}
753
James Feist97a63f12018-05-17 13:50:57 -0700754void postToDbus(const nlohmann::json &newConfiguration,
755 nlohmann::json &systemConfiguration,
James Feist8f2710a2018-05-09 17:18:55 -0700756 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800757
James Feist1b2e2242018-01-30 13:45:19 -0800758{
James Feist97a63f12018-05-17 13:50:57 -0700759 // iterate through boards
760 for (auto &boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800761 {
762 std::string boardKey = boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700763 std::vector<std::string> path;
764 std::string jsonPointerPath = "/" + boardKey;
765 // loop through newConfiguration, but use values from system
766 // configuration to be able to modify via dbus later
767 auto boardValues = systemConfiguration[boardKey];
James Feist1b2e2242018-01-30 13:45:19 -0800768 auto findBoardType = boardValues.find("type");
769 std::string boardType;
770 if (findBoardType != boardValues.end() &&
771 findBoardType->type() == nlohmann::json::value_t::string)
772 {
773 boardType = findBoardType->get<std::string>();
774 std::regex_replace(boardType.begin(), boardType.begin(),
775 boardType.end(), ILLEGAL_DBUS_REGEX, "_");
776 }
777 else
778 {
779 std::cerr << "Unable to find type for " << boardKey
780 << " reverting to Chassis.\n";
781 boardType = "Chassis";
782 }
James Feist11be6672018-04-06 14:05:32 -0700783 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800784
785 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
786 ILLEGAL_DBUS_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700787 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
788 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800789
James Feist8f2710a2018-05-09 17:18:55 -0700790 auto inventoryIface = objServer.add_interface(
791 boardName, "xyz.openbmc_project.Inventory.Item");
792 auto boardIface = objServer.add_interface(
793 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700794
James Feist97a63f12018-05-17 13:50:57 -0700795 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
796 boardIface.get(), boardValues, objServer);
797 jsonPointerPath += "/";
798 // iterate through board properties
James Feist9eb0b582018-04-27 12:15:46 -0700799 for (auto &boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700800 {
801 if (boardField.value().type() == nlohmann::json::value_t::object)
802 {
James Feist8f2710a2018-05-09 17:18:55 -0700803 auto iface =
804 objServer.add_interface(boardName, boardField.key());
James Feist97a63f12018-05-17 13:50:57 -0700805 populateInterfaceFromJson(
806 systemConfiguration, jsonPointerPath + boardField.key(),
807 iface.get(), boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700808 }
809 }
James Feist97a63f12018-05-17 13:50:57 -0700810
James Feist1b2e2242018-01-30 13:45:19 -0800811 auto exposes = boardValues.find("exposes");
812 if (exposes == boardValues.end())
813 {
814 continue;
815 }
James Feist97a63f12018-05-17 13:50:57 -0700816 // iterate through exposes
817 jsonPointerPath += "exposes/";
818
819 // store the board level pointer so we can modify it on the way down
820 std::string jsonPointerPathBoard = jsonPointerPath;
821 size_t exposesIndex = -1;
James Feist1b2e2242018-01-30 13:45:19 -0800822 for (auto &item : *exposes)
823 {
James Feist97a63f12018-05-17 13:50:57 -0700824 exposesIndex++;
825 jsonPointerPath = jsonPointerPathBoard;
826 jsonPointerPath += std::to_string(exposesIndex);
827
James Feist1b2e2242018-01-30 13:45:19 -0800828 auto findName = item.find("name");
829 if (findName == item.end())
830 {
831 std::cerr << "cannot find name in field " << item << "\n";
832 continue;
833 }
834 auto findStatus = item.find("status");
835 // if status is not found it is assumed to be status = 'okay'
836 if (findStatus != item.end())
837 {
838 if (*findStatus == "disabled")
839 {
840 continue;
841 }
842 }
843 auto findType = item.find("type");
844 std::string itemType;
845 if (findType != item.end())
846 {
847 itemType = findType->get<std::string>();
848 std::regex_replace(itemType.begin(), itemType.begin(),
849 itemType.end(), ILLEGAL_DBUS_REGEX, "_");
850 }
851 else
852 {
853 itemType = "unknown";
854 }
855 std::string itemName = findName->get<std::string>();
856 std::regex_replace(itemName.begin(), itemName.begin(),
857 itemName.end(), ILLEGAL_DBUS_REGEX, "_");
James Feist8f2710a2018-05-09 17:18:55 -0700858 auto itemIface = objServer.add_interface(
859 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800860 "xyz.openbmc_project.Configuration." + itemType);
861
James Feist97a63f12018-05-17 13:50:57 -0700862 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
863 itemIface.get(), item, objServer);
James Feist1b2e2242018-01-30 13:45:19 -0800864
James Feist9eb0b582018-04-27 12:15:46 -0700865 for (auto &objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800866 {
James Feist97a63f12018-05-17 13:50:57 -0700867 jsonPointerPath = jsonPointerPathBoard +
868 std::to_string(exposesIndex) + "/" +
869 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -0800870 if (objectPair.value().type() ==
871 nlohmann::json::value_t::object)
872 {
James Feist8f2710a2018-05-09 17:18:55 -0700873 auto objectIface = objServer.add_interface(
874 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800875 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -0700876 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -0700877
878 populateInterfaceFromJson(
879 systemConfiguration, jsonPointerPath, objectIface.get(),
880 objectPair.value(), objServer);
James Feist1b2e2242018-01-30 13:45:19 -0800881 }
882 else if (objectPair.value().type() ==
883 nlohmann::json::value_t::array)
884 {
885 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -0700886 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -0800887 {
James Feist8f2710a2018-05-09 17:18:55 -0700888 continue;
889 }
890 bool isLegal = true;
891 auto type = objectPair.value()[0].type();
892 if (type != nlohmann::json::value_t::object)
893 {
894 continue;
895 }
896
897 // verify legal json
898 for (const auto &arrayItem : objectPair.value())
899 {
900 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800901 {
James Feist8f2710a2018-05-09 17:18:55 -0700902 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800903 break;
904 }
James Feist8f2710a2018-05-09 17:18:55 -0700905 }
906 if (!isLegal)
907 {
908 std::cerr << "dbus format error" << objectPair.value()
909 << "\n";
910 break;
911 }
912
913 for (auto &arrayItem : objectPair.value())
914 {
James Feist97a63f12018-05-17 13:50:57 -0700915 // limit what interfaces accept set for saftey
916 bool setable = std::find(SETTABLE_INTERFACES.begin(),
917 SETTABLE_INTERFACES.end(),
918 objectPair.key()) !=
919 SETTABLE_INTERFACES.end();
920
James Feist8f2710a2018-05-09 17:18:55 -0700921 auto objectIface = objServer.add_interface(
922 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800923 "xyz.openbmc_project.Configuration." + itemType +
James Feist97a63f12018-05-17 13:50:57 -0700924 "." + objectPair.key() +
James Feist8f2710a2018-05-09 17:18:55 -0700925 std::to_string(index++));
James Feist97a63f12018-05-17 13:50:57 -0700926 populateInterfaceFromJson(
927 systemConfiguration,
928 jsonPointerPath + "/" + std::to_string(index),
929 objectIface.get(), arrayItem, objServer, setable);
James Feist1b2e2242018-01-30 13:45:19 -0800930 }
931 }
932 }
933 }
934 }
935}
936
937// finds the template character (currently set to $) and replaces the value with
938// the field found in a dbus object i.e. $ADDRESS would get populated with the
939// ADDRESS field from a object on dbus
940void templateCharReplace(
941 nlohmann::json::iterator &keyPair,
James Feist8f2710a2018-05-09 17:18:55 -0700942 const boost::container::flat_map<std::string, BasicVariantType>
James Feist1b2e2242018-01-30 13:45:19 -0800943 &foundDevice,
944 size_t &foundDeviceIdx)
945{
James Feist11be6672018-04-06 14:05:32 -0700946 if (keyPair.value().type() == nlohmann::json::value_t::object)
947 {
948 for (auto nextLayer = keyPair.value().begin();
949 nextLayer != keyPair.value().end(); nextLayer++)
950 {
951 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
952 }
953 return;
954 }
955 else if (keyPair.value().type() != nlohmann::json::value_t::string)
James Feist1b2e2242018-01-30 13:45:19 -0800956 {
957 return;
958 }
959
960 std::string value = keyPair.value();
961 if (value.find(TEMPLATE_CHAR) != std::string::npos)
962 {
963 std::string templateValue = value;
964
965 templateValue.erase(0, 1); // remove template character
966
967 // special case index
968 if ("index" == templateValue)
969 {
970 keyPair.value() = foundDeviceIdx;
971 }
972 else
973 {
James Feist13b86d62018-05-29 11:24:35 -0700974 bool found = false;
James Feist1b2e2242018-01-30 13:45:19 -0800975 for (auto &foundDevicePair : foundDevice)
976 {
977 if (boost::iequals(foundDevicePair.first, templateValue))
978 {
James Feist13b86d62018-05-29 11:24:35 -0700979 mapbox::util::apply_visitor(
980 [&](auto &&val) { keyPair.value() = val; },
981 foundDevicePair.second);
982 found = true;
James Feist1b2e2242018-01-30 13:45:19 -0800983 break;
984 }
985 }
James Feist13b86d62018-05-29 11:24:35 -0700986 if (!found)
James Feist1b2e2242018-01-30 13:45:19 -0800987 {
988 std::cerr << "could not find symbol " << templateValue << "\n";
989 }
James Feist1b2e2242018-01-30 13:45:19 -0800990 }
991 }
992}
993
James Feist8f2710a2018-05-09 17:18:55 -0700994// reads json files out of the filesystem
995bool findJsonFiles(std::list<nlohmann::json> &configurations)
James Feist3cb5fec2018-01-23 14:41:51 -0800996{
997 // find configuration files
998 std::vector<fs::path> jsonPaths;
999 if (!find_files(fs::path(CONFIGURATION_DIR), R"(.*\.json)", jsonPaths, 0))
1000 {
1001 std::cerr << "Unable to find any configuration files in "
1002 << CONFIGURATION_DIR << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001003 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001004 }
James Feist3cb5fec2018-01-23 14:41:51 -08001005 for (auto &jsonPath : jsonPaths)
1006 {
1007 std::ifstream jsonStream(jsonPath.c_str());
1008 if (!jsonStream.good())
1009 {
1010 std::cerr << "unable to open " << jsonPath.string() << "\n";
1011 continue;
1012 }
1013 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1014 if (data.is_discarded())
1015 {
1016 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1017 continue;
1018 }
1019 if (data.type() == nlohmann::json::value_t::array)
1020 {
1021 for (auto &d : data)
1022 {
1023 configurations.emplace_back(d);
1024 }
1025 }
1026 else
1027 {
1028 configurations.emplace_back(data);
1029 }
1030 }
James Feist75fdeeb2018-02-20 14:26:16 -08001031}
James Feist3cb5fec2018-01-23 14:41:51 -08001032
James Feist8f2710a2018-05-09 17:18:55 -07001033struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001034{
James Feist75fdeeb2018-02-20 14:26:16 -08001035
James Feist8f2710a2018-05-09 17:18:55 -07001036 PerformScan(nlohmann::json &systemConfiguration,
1037 std::list<nlohmann::json> &configurations,
1038 std::function<void(void)> &&callback) :
1039 _systemConfiguration(systemConfiguration),
1040 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001041 {
James Feist8f2710a2018-05-09 17:18:55 -07001042 }
1043 void run()
1044 {
1045 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001046 {
James Feist1b2e2242018-01-30 13:45:19 -08001047 auto findProbe = it->find("probe");
1048 auto findName = it->find("name");
James Feist3cb5fec2018-01-23 14:41:51 -08001049
James Feist1b2e2242018-01-30 13:45:19 -08001050 nlohmann::json probeCommand;
1051 // check for poorly formatted fields, probe must be an array
1052 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001053 {
1054 std::cerr << "configuration file missing probe:\n " << *it
1055 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001056 it = _configurations.erase(it);
1057 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001058 }
James Feist1b2e2242018-01-30 13:45:19 -08001059 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001060 {
1061 probeCommand = nlohmann::json::array();
1062 probeCommand.push_back(*findProbe);
1063 }
1064 else
1065 {
1066 probeCommand = *findProbe;
1067 }
James Feist1b2e2242018-01-30 13:45:19 -08001068
1069 if (findName == it->end())
1070 {
1071 std::cerr << "configuration file missing name:\n " << *it
1072 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001073 it = _configurations.erase(it);
1074 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001075 }
James Feist8f2710a2018-05-09 17:18:55 -07001076 std::string name = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001077
James Feist8f2710a2018-05-09 17:18:55 -07001078 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(), name) !=
1079 PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001080 {
James Feist8f2710a2018-05-09 17:18:55 -07001081 it = _configurations.erase(it);
1082 continue;
1083 }
1084 nlohmann::json *record = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001085
James Feist8f2710a2018-05-09 17:18:55 -07001086 // store reference to this to children to makes sure we don't get
1087 // destroyed too early
1088 auto thisRef = shared_from_this();
1089 auto p = std::make_shared<PerformProbe>(
1090 probeCommand,
1091 [&, record, name,
1092 thisRef](std::vector<boost::container::flat_map<
1093 std::string, BasicVariantType>> &foundDevices) {
1094 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001095
James Feist8f2710a2018-05-09 17:18:55 -07001096 PASSED_PROBES.push_back(name);
1097 size_t foundDeviceIdx = 0;
1098
James Feistbe5425f2018-06-08 10:30:55 -07001099 // insert into configuration temporarly to be able to
1100 // reference ourselves
1101 _systemConfiguration[name] = *record;
1102
James Feist8f2710a2018-05-09 17:18:55 -07001103 for (auto &foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001104 {
James Feist8f2710a2018-05-09 17:18:55 -07001105 for (auto keyPair = record->begin();
1106 keyPair != record->end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001107 {
James Feist1b2e2242018-01-30 13:45:19 -08001108 templateCharReplace(keyPair, foundDevice,
1109 foundDeviceIdx);
James Feist8f2710a2018-05-09 17:18:55 -07001110 }
1111 auto findExpose = record->find("exposes");
1112 if (findExpose == record->end())
1113 {
1114 continue;
1115 }
1116 for (auto &expose : *findExpose)
1117 {
1118 for (auto keyPair = expose.begin();
1119 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001120 {
James Feist1b2e2242018-01-30 13:45:19 -08001121
James Feist8f2710a2018-05-09 17:18:55 -07001122 // fill in template characters with devices
1123 // found
1124 templateCharReplace(keyPair, foundDevice,
1125 foundDeviceIdx);
1126 // special case bind
1127 if (boost::starts_with(keyPair.key(), "bind_"))
1128 {
1129 if (keyPair.value().type() !=
1130 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001131 {
James Feist8f2710a2018-05-09 17:18:55 -07001132 std::cerr << "bind_ value must be of "
1133 "type string "
1134 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001135 continue;
1136 }
James Feist8f2710a2018-05-09 17:18:55 -07001137 bool foundBind = false;
1138 std::string bind = keyPair.key().substr(
1139 sizeof("bind_") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001140
James Feist8f2710a2018-05-09 17:18:55 -07001141 for (auto &configurationPair :
1142 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001143 {
James Feist1b2e2242018-01-30 13:45:19 -08001144
James Feist8f2710a2018-05-09 17:18:55 -07001145 auto configListFind =
1146 configurationPair.value().find(
1147 "exposes");
1148
1149 if (configListFind ==
1150 configurationPair.value()
1151 .end() ||
1152 configListFind->type() !=
1153 nlohmann::json::value_t::array)
1154 {
1155 continue;
1156 }
1157 for (auto &exposedObject :
1158 *configListFind)
1159 {
1160 std::string foundObjectName =
1161 (exposedObject)["name"];
1162 if (boost::iequals(
1163 foundObjectName,
1164 keyPair.value()
1165 .get<std::string>()))
1166 {
1167 exposedObject["status"] =
1168 "okay";
1169 expose[bind] = exposedObject;
1170
1171 foundBind = true;
1172 break;
1173 }
1174 }
1175 if (foundBind)
1176 {
James Feist3cb5fec2018-01-23 14:41:51 -08001177 break;
1178 }
1179 }
James Feist8f2710a2018-05-09 17:18:55 -07001180 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001181 {
James Feist8f2710a2018-05-09 17:18:55 -07001182 std::cerr << "configuration file "
1183 "dependency error, "
1184 "could not find bind "
1185 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001186 }
1187 }
1188 }
1189 }
1190 }
James Feistbe5425f2018-06-08 10:30:55 -07001191 // overwrite ourselves with cleaned up version
James Feist8f2710a2018-05-09 17:18:55 -07001192 _systemConfiguration[name] = *record;
1193 });
1194 p->run();
1195 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001196 }
1197 }
James Feist75fdeeb2018-02-20 14:26:16 -08001198
James Feist8f2710a2018-05-09 17:18:55 -07001199 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001200 {
James Feist8f2710a2018-05-09 17:18:55 -07001201 if (_passed)
1202 {
1203 auto nextScan = std::make_shared<PerformScan>(
1204 _systemConfiguration, _configurations, std::move(_callback));
1205 nextScan->run();
1206 }
1207 else
1208 {
1209 _callback();
1210 }
1211 }
1212 nlohmann::json &_systemConfiguration;
1213 std::list<nlohmann::json> _configurations;
1214 std::function<void(void)> _callback;
1215 std::vector<std::shared_ptr<PerformProbe>> _probes;
1216 bool _passed = false;
1217};
James Feistc95cb142018-02-26 10:41:42 -08001218
James Feist8f2710a2018-05-09 17:18:55 -07001219// main properties changed entry
1220void propertiesChangedCallback(
1221 boost::asio::io_service &io,
1222 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1223 nlohmann::json &systemConfiguration,
1224 sdbusplus::asio::object_server &objServer)
1225{
1226 static boost::asio::deadline_timer timer(io);
1227 timer.expires_from_now(boost::posix_time::seconds(1));
1228
1229 // setup an async wait as we normally get flooded with new requests
1230 timer.async_wait([&](const boost::system::error_code &ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001231 if (ec == boost::asio::error::operation_aborted)
1232 {
1233 // we were cancelled
1234 return;
1235 }
1236 else if (ec)
1237 {
1238 std::cerr << "async wait error " << ec << "\n";
1239 return;
1240 }
1241
1242 nlohmann::json oldConfiguration = systemConfiguration;
1243 DBUS_PROBE_OBJECTS.clear();
1244
1245 std::list<nlohmann::json> configurations;
1246 if (!findJsonFiles(configurations))
1247 {
1248 std::cerr << "cannot find json files\n";
1249 return;
1250 }
1251
1252 auto perfScan = std::make_shared<PerformScan>(
1253 systemConfiguration, configurations, [&, oldConfiguration]() {
1254 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001255 for (auto it = newConfiguration.begin();
1256 it != newConfiguration.end();)
1257 {
1258 auto findKey = oldConfiguration.find(it.key());
1259 if (findKey != oldConfiguration.end())
1260 {
1261 it = newConfiguration.erase(it);
1262 }
1263 else
1264 {
1265 it++;
1266 }
1267 }
James Feist8f2710a2018-05-09 17:18:55 -07001268 registerCallbacks(io, dbusMatches, systemConfiguration,
1269 objServer);
1270 io.post([&, newConfiguration]() {
1271 // todo: for now, only add new configurations,
1272 // unload to come later unloadOverlays();
1273 loadOverlays(newConfiguration);
1274 io.post([&]() { writeJsonFiles(systemConfiguration); });
1275 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001276 postToDbus(newConfiguration, systemConfiguration,
1277 objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001278 });
1279 });
1280 });
1281 perfScan->run();
1282 });
James Feist75fdeeb2018-02-20 14:26:16 -08001283}
1284
James Feist8f2710a2018-05-09 17:18:55 -07001285void registerCallbacks(boost::asio::io_service &io,
1286 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1287 nlohmann::json &systemConfiguration,
1288 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001289{
1290 static boost::container::flat_set<std::string> watchedObjects;
1291
1292 for (const auto &objectMap : DBUS_PROBE_OBJECTS)
1293 {
1294 auto findObject = watchedObjects.find(objectMap.first);
1295 if (findObject != watchedObjects.end())
1296 {
1297 continue;
1298 }
James Feist8f2710a2018-05-09 17:18:55 -07001299 std::function<void(sdbusplus::message::message & message)>
1300 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001301
James Feist8f2710a2018-05-09 17:18:55 -07001302 [&](sdbusplus::message::message &) {
1303 propertiesChangedCallback(io, dbusMatches,
1304 systemConfiguration, objServer);
1305 };
1306
1307 sdbusplus::bus::match::match match(
1308 static_cast<sdbusplus::bus::bus &>(*SYSTEM_BUS),
1309 "type='signal',member='PropertiesChanged',arg0='" +
1310 objectMap.first + "'",
1311 eventHandler);
1312 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001313 }
1314}
1315
1316int main(int argc, char **argv)
1317{
1318 // setup connection to dbus
1319 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001320 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001321 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001322
James Feist8f2710a2018-05-09 17:18:55 -07001323 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001324
James Feist8f2710a2018-05-09 17:18:55 -07001325 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1326 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1327 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001328
James Feist8f2710a2018-05-09 17:18:55 -07001329 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1330 objServer.add_interface("/xyz/openbmc_project/inventory",
1331 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001332
1333 // to keep reference to the match / filter objects so they don't get
1334 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001335 std::vector<sdbusplus::bus::match::match> dbusMatches;
1336
1337 nlohmann::json systemConfiguration = nlohmann::json::object();
1338
1339 inventoryIface->register_method(
1340 "Notify", [](const boost::container::flat_map<
1341 std::string,
1342 boost::container::flat_map<std::string, BasicVariantType>>
1343 &object) { return; });
1344 inventoryIface->initialize();
1345
1346 io.post([&]() {
1347 unloadAllOverlays();
1348 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1349 objServer);
1350 });
James Feist4131aea2018-03-09 09:47:30 -08001351
James Feistfd1264a2018-05-03 12:10:00 -07001352 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001353 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1354 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001355 });
James Feist8f2710a2018-05-09 17:18:55 -07001356 entityIface->initialize();
1357
James Feist1b2e2242018-01-30 13:45:19 -08001358 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001359
1360 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001361}