blob: 81df6e12dbac9be669dc26bfac54cb5810e249a2 [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/";
James Feistb4383f42018-08-06 16:54:10 -070036constexpr const char *configurationDirectory = "/usr/share/configurations";
37constexpr const char *schemaDirectory = "/usr/share/configurations/schemas";
38constexpr const char *globalSchema = "global.json";
James Feist3cb5fec2018-01-23 14:41:51 -080039constexpr const char *TEMPLATE_CHAR = "$";
James Feistc95cb142018-02-26 10:41:42 -080040constexpr const size_t PROPERTIES_CHANGED_UNTIL_FLUSH_COUNT = 20;
James Feist8f2710a2018-05-09 17:18:55 -070041constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist4131aea2018-03-09 09:47:30 -080042constexpr const size_t SLEEP_AFTER_PROPERTIES_CHANGE_SECONDS = 5;
James Feist3cb5fec2018-01-23 14:41:51 -080043
44namespace fs = std::experimental::filesystem;
45struct cmp_str
46{
47 bool operator()(const char *a, const char *b) const
48 {
49 return std::strcmp(a, b) < 0;
50 }
51};
52
James Feist8f2710a2018-05-09 17:18:55 -070053struct PerformProbe;
54
James Feist3cb5fec2018-01-23 14:41:51 -080055// underscore T for collison with dbus c api
56enum class probe_type_codes
57{
58 FALSE_T,
59 TRUE_T,
60 AND,
61 OR,
James Feist6bd2a022018-03-13 12:30:58 -070062 FOUND,
63 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080064};
65const static boost::container::flat_map<const char *, probe_type_codes, cmp_str>
66 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
67 {"TRUE", probe_type_codes::TRUE_T},
68 {"AND", probe_type_codes::AND},
69 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070070 {"FOUND", probe_type_codes::FOUND},
71 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080072
James Feist97a63f12018-05-17 13:50:57 -070073static constexpr std::array<const char *, 1> SETTABLE_INTERFACES = {
James Feist1e3e6982018-08-03 16:09:28 -070074 "Thresholds"};
James Feist97a63f12018-05-17 13:50:57 -070075
James Feist8f2710a2018-05-09 17:18:55 -070076using BasicVariantType =
77 sdbusplus::message::variant<std::string, int64_t, uint64_t, double, int32_t,
78 uint32_t, int16_t, uint16_t, uint8_t, bool>;
79
James Feist3cb5fec2018-01-23 14:41:51 -080080using GetSubTreeType = std::vector<
81 std::pair<std::string,
82 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
83
84using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070085 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080086 boost::container::flat_map<
87 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070088 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080089
90boost::container::flat_map<
91 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070092 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -080093 DBUS_PROBE_OBJECTS;
94std::vector<std::string> PASSED_PROBES;
95
96// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -070097std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist3cb5fec2018-01-23 14:41:51 -080098
James Feista6750242018-07-16 14:12:27 -070099const std::regex ILLEGAL_DBUS_REGEX("[^A-Za-z0-9_.]");
James Feist1b2e2242018-01-30 13:45:19 -0800100
James Feist8f2710a2018-05-09 17:18:55 -0700101void registerCallbacks(boost::asio::io_service &io,
102 std::vector<sdbusplus::bus::match::match> &dbusMatches,
103 nlohmann::json &systemConfiguration,
104 sdbusplus::asio::object_server &objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800105
James Feist3cb5fec2018-01-23 14:41:51 -0800106// calls the mapper to find all exposed objects of an interface type
107// and creates a vector<flat_map> that contains all the key value pairs
108// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700109void findDbusObjects(std::shared_ptr<PerformProbe> probe,
110 std::shared_ptr<sdbusplus::asio::connection> connection,
111 std::string &interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800112{
James Feist8f2710a2018-05-09 17:18:55 -0700113
114 // store reference to pending callbacks so we don't overwhelm services
115 static boost::container::flat_map<
116 std::string, std::vector<std::shared_ptr<PerformProbe>>>
117 pendingProbes;
118
119 if (DBUS_PROBE_OBJECTS[interface].size())
120 {
121 return;
122 }
123
124 // add shared_ptr to vector of Probes waiting for callback from a specific
125 // interface to keep alive while waiting for response
126 std::array<const char *, 1> objects = {interface.c_str()};
127 std::vector<std::shared_ptr<PerformProbe>> &pending =
128 pendingProbes[interface];
129 auto iter = pending.emplace(pending.end(), probe);
130 // only allow first call to run to not overwhelm processes
131 if (iter != pending.begin())
132 {
133 return;
134 }
135
James Feist3cb5fec2018-01-23 14:41:51 -0800136 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700137 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700138 [connection, interface, probe](boost::system::error_code &ec,
139 const GetSubTreeType &interfaceSubtree) {
140 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700141 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700142 {
James Feist0de40152018-07-25 11:56:12 -0700143 pendingProbes[interface].clear();
144 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700145 {
James Feist0de40152018-07-25 11:56:12 -0700146 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700147 }
James Feist0de40152018-07-25 11:56:12 -0700148 std::cerr << "Error communicating to mapper.\n";
149
150 // if we can't communicate to the mapper something is very wrong
151 std::exit(EXIT_FAILURE);
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(
James Feist0de40152018-07-25 11:56:12 -0700168 [conn,
James Feist8f2710a2018-05-09 17:18:55 -0700169 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;
James Feist3cb5fec2018-01-23 14:41:51 -0800422 }
423
424 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800425 if (ret && foundDevs.size() == 0)
426 {
427 foundDevs.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700428 boost::container::flat_map<std::string, BasicVariantType>());
James Feist3cb5fec2018-01-23 14:41:51 -0800429 }
James Feist6bd2a022018-03-13 12:30:58 -0700430 if (matchOne && foundDevs.size() > 1)
431 {
432 foundDevs.erase(foundDevs.begin() + 1, foundDevs.end());
433 }
James Feist3cb5fec2018-01-23 14:41:51 -0800434 return ret;
435}
James Feist8f2710a2018-05-09 17:18:55 -0700436// this class finds the needed dbus fields and on destruction runs the probe
437struct PerformProbe : std::enable_shared_from_this<PerformProbe>
438{
James Feist3cb5fec2018-01-23 14:41:51 -0800439
James Feist8f2710a2018-05-09 17:18:55 -0700440 PerformProbe(
441 const std::vector<std::string> &probeCommand,
442 std::function<void(std::vector<boost::container::flat_map<
443 std::string, BasicVariantType>> &)> &&callback) :
444 _probeCommand(probeCommand),
445 _callback(std::move(callback))
446 {
447 }
448 ~PerformProbe()
449 {
450 if (probe(_probeCommand, _foundDevs))
451 {
452 _callback(_foundDevs);
453 }
454 }
455 void run()
456 {
457 // parse out dbus probes by discarding other probe types
458 boost::container::flat_map<const char *, probe_type_codes,
459 cmp_str>::const_iterator probeType;
460
461 std::vector<std::string> dbusProbes;
462 for (std::string &probe : _probeCommand)
463 {
464 bool found = false;
465 boost::container::flat_map<const char *, probe_type_codes,
466 cmp_str>::const_iterator probeType;
467 for (probeType = PROBE_TYPES.begin();
468 probeType != PROBE_TYPES.end(); probeType++)
469 {
470 if (probe.find(probeType->first) != std::string::npos)
471 {
472 found = true;
473 break;
474 }
475 }
476 if (found)
477 {
478 continue;
479 }
480 // syntax requires probe before first open brace
481 auto findStart = probe.find("(");
482 std::string interface = probe.substr(0, findStart);
483
484 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
485 }
486 }
487 std::vector<std::string> _probeCommand;
488 std::function<void(
489 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
490 &)>
491 _callback;
492 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
493 _foundDevs;
494};
495
496// writes output files to persist data
James Feistbb43d022018-06-12 15:44:33 -0700497bool writeJsonFiles(const nlohmann::json &systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800498{
499 std::experimental::filesystem::create_directory(OUTPUT_DIR);
500 std::ofstream output(std::string(OUTPUT_DIR) + "system.json");
James Feistbb43d022018-06-12 15:44:33 -0700501 if (!output.good())
502 {
503 return false;
504 }
James Feist1b2e2242018-01-30 13:45:19 -0800505 output << systemConfiguration.dump(4);
506 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700507 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700508}
James Feist1b2e2242018-01-30 13:45:19 -0800509
James Feist8f2710a2018-05-09 17:18:55 -0700510// template function to add array as dbus property
511template <typename PropertyType>
512void addArrayToDbus(const std::string &name, const nlohmann::json &array,
James Feistbb43d022018-06-12 15:44:33 -0700513 sdbusplus::asio::dbus_interface *iface,
514 sdbusplus::asio::PropertyPermission permission)
James Feist8f2710a2018-05-09 17:18:55 -0700515{
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 Feistbb43d022018-06-12 15:44:33 -0700525 // todo(james), currently there are no reason to persist arrays, get around
526 // to it if needed
527
528 iface->register_property(name, values, permission);
James Feist1b2e2242018-01-30 13:45:19 -0800529}
James Feist97a63f12018-05-17 13:50:57 -0700530
531template <typename JsonType>
James Feistbb43d022018-06-12 15:44:33 -0700532bool setJsonFromPointer(const std::string &ptrStr, const JsonType &value,
James Feist97a63f12018-05-17 13:50:57 -0700533 nlohmann::json &systemConfiguration)
534{
535 try
536 {
537 nlohmann::json::json_pointer ptr(ptrStr);
538 nlohmann::json &ref = systemConfiguration[ptr];
539 ref = value;
540 return true;
541 }
542 catch (const std::out_of_range)
543 {
544 return false;
545 }
546}
James Feistbb43d022018-06-12 15:44:33 -0700547
548template <typename PropertyType>
549void addProperty(const std::string &propertyName, const PropertyType &value,
550 sdbusplus::asio::dbus_interface *iface,
551 nlohmann::json &systemConfiguration,
552 const std::string &jsonPointerString,
553 sdbusplus::asio::PropertyPermission permission)
554{
555 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
556 {
557 iface->register_property(propertyName, value);
558 return;
559 }
James Feist0de40152018-07-25 11:56:12 -0700560 iface->register_property(propertyName, value, [
561 &systemConfiguration, jsonPointerString{std::string(jsonPointerString)}
562 ](const PropertyType &newVal, PropertyType &val) {
563 val = newVal;
564 if (!setJsonFromPointer(jsonPointerString, val, systemConfiguration))
565 {
566 std::cerr << "error setting json field\n";
James Feistbb43d022018-06-12 15:44:33 -0700567 return -1;
James Feist0de40152018-07-25 11:56:12 -0700568 }
569 if (writeJsonFiles(systemConfiguration))
570 {
571 std::cerr << "error setting json file\n";
572 return 1;
573 }
574 return -1;
575 });
James Feistbb43d022018-06-12 15:44:33 -0700576}
577
James Feist1b2e2242018-01-30 13:45:19 -0800578// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700579void populateInterfaceFromJson(
580 nlohmann::json &systemConfiguration, const std::string &jsonPointerPath,
581 sdbusplus::asio::dbus_interface *iface, nlohmann::json &dict,
582 sdbusplus::asio::object_server &objServer,
583 sdbusplus::asio::PropertyPermission permission =
584 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800585{
James Feist9eb0b582018-04-27 12:15:46 -0700586 for (auto &dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800587 {
James Feist8f2710a2018-05-09 17:18:55 -0700588 auto type = dictPair.value().type();
589 bool array = false;
590 if (dictPair.value().type() == nlohmann::json::value_t::array)
591 {
592 array = true;
593 if (!dictPair.value().size())
594 {
595 continue;
596 }
597 type = dictPair.value()[0].type();
598 bool isLegal = true;
599 for (const auto &arrayItem : dictPair.value())
600 {
601 if (arrayItem.type() != type)
602 {
603 isLegal = false;
604 break;
605 }
606 }
607 if (!isLegal)
608 {
609 std::cerr << "dbus format error" << dictPair.value() << "\n";
610 continue;
611 }
612 if (type == nlohmann::json::value_t::object)
613 {
614 continue; // handled elsewhere
615 }
616 }
James Feist97a63f12018-05-17 13:50:57 -0700617 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700618 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
619 {
620 // all setable numbers are doubles as it is difficult to always
621 // create a configuration file with all whole numbers as decimals
622 // i.e. 1.0
623 if (dictPair.value().is_number())
624 {
625 type = nlohmann::json::value_t::number_float;
626 }
627 }
628
James Feist8f2710a2018-05-09 17:18:55 -0700629 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800630 {
James Feist9eb0b582018-04-27 12:15:46 -0700631 case (nlohmann::json::value_t::boolean):
632 {
James Feist8f2710a2018-05-09 17:18:55 -0700633 if (array)
634 {
635 // todo: array of bool isn't detected correctly by
636 // sdbusplus, change it to numbers
637 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700638 iface, permission);
James Feist8f2710a2018-05-09 17:18:55 -0700639 }
James Feistbb43d022018-06-12 15:44:33 -0700640
James Feist97a63f12018-05-17 13:50:57 -0700641 else
642 {
James Feistbb43d022018-06-12 15:44:33 -0700643 addProperty(dictPair.key(), dictPair.value().get<bool>(),
644 iface, systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700645 }
James Feist9eb0b582018-04-27 12:15:46 -0700646 break;
647 }
648 case (nlohmann::json::value_t::number_integer):
649 {
James Feist8f2710a2018-05-09 17:18:55 -0700650 if (array)
651 {
652 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700653 iface, permission);
James Feist97a63f12018-05-17 13:50:57 -0700654 }
655 else
656 {
James Feistbb43d022018-06-12 15:44:33 -0700657 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
658 iface, systemConfiguration, key,
659 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700660 }
James Feist9eb0b582018-04-27 12:15:46 -0700661 break;
662 }
663 case (nlohmann::json::value_t::number_unsigned):
664 {
James Feist8f2710a2018-05-09 17:18:55 -0700665 if (array)
666 {
667 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700668 iface, permission);
James Feist97a63f12018-05-17 13:50:57 -0700669 }
670 else
671 {
James Feistbb43d022018-06-12 15:44:33 -0700672 addProperty(dictPair.key(),
673 dictPair.value().get<uint64_t>(), iface,
674 systemConfiguration, key,
675 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700676 }
James Feist9eb0b582018-04-27 12:15:46 -0700677 break;
678 }
679 case (nlohmann::json::value_t::number_float):
680 {
James Feist8f2710a2018-05-09 17:18:55 -0700681 if (array)
682 {
683 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700684 iface, permission);
James Feist8f2710a2018-05-09 17:18:55 -0700685 }
James Feistbb43d022018-06-12 15:44:33 -0700686
James Feist97a63f12018-05-17 13:50:57 -0700687 else
688 {
James Feistbb43d022018-06-12 15:44:33 -0700689 addProperty(dictPair.key(), dictPair.value().get<double>(),
690 iface, systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700691 }
James Feist9eb0b582018-04-27 12:15:46 -0700692 break;
693 }
694 case (nlohmann::json::value_t::string):
695 {
James Feist8f2710a2018-05-09 17:18:55 -0700696 if (array)
697 {
James Feistbb43d022018-06-12 15:44:33 -0700698 addArrayToDbus<std::string>(
699 dictPair.key(), dictPair.value(), iface, permission);
James Feist97a63f12018-05-17 13:50:57 -0700700 }
701 else
702 {
James Feistbb43d022018-06-12 15:44:33 -0700703 addProperty(dictPair.key(),
704 dictPair.value().get<std::string>(), iface,
705 systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700706 }
James Feist9eb0b582018-04-27 12:15:46 -0700707 break;
708 }
James Feist1b2e2242018-01-30 13:45:19 -0800709 }
710 }
James Feist1b2e2242018-01-30 13:45:19 -0800711
James Feist8f2710a2018-05-09 17:18:55 -0700712 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800713}
714
James Feist97a63f12018-05-17 13:50:57 -0700715void postToDbus(const nlohmann::json &newConfiguration,
716 nlohmann::json &systemConfiguration,
James Feist8f2710a2018-05-09 17:18:55 -0700717 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800718
James Feist1b2e2242018-01-30 13:45:19 -0800719{
James Feist97a63f12018-05-17 13:50:57 -0700720 // iterate through boards
721 for (auto &boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800722 {
723 std::string boardKey = boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700724 std::vector<std::string> path;
725 std::string jsonPointerPath = "/" + boardKey;
726 // loop through newConfiguration, but use values from system
727 // configuration to be able to modify via dbus later
728 auto boardValues = systemConfiguration[boardKey];
James Feistd63d18a2018-07-19 15:23:45 -0700729 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800730 std::string boardType;
731 if (findBoardType != boardValues.end() &&
732 findBoardType->type() == nlohmann::json::value_t::string)
733 {
734 boardType = findBoardType->get<std::string>();
735 std::regex_replace(boardType.begin(), boardType.begin(),
736 boardType.end(), ILLEGAL_DBUS_REGEX, "_");
737 }
738 else
739 {
740 std::cerr << "Unable to find type for " << boardKey
741 << " reverting to Chassis.\n";
742 boardType = "Chassis";
743 }
James Feist11be6672018-04-06 14:05:32 -0700744 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800745
746 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
747 ILLEGAL_DBUS_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700748 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
749 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800750
James Feist8f2710a2018-05-09 17:18:55 -0700751 auto inventoryIface = objServer.add_interface(
752 boardName, "xyz.openbmc_project.Inventory.Item");
753 auto boardIface = objServer.add_interface(
754 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700755
James Feist97a63f12018-05-17 13:50:57 -0700756 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
757 boardIface.get(), boardValues, objServer);
758 jsonPointerPath += "/";
759 // iterate through board properties
James Feist9eb0b582018-04-27 12:15:46 -0700760 for (auto &boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700761 {
762 if (boardField.value().type() == nlohmann::json::value_t::object)
763 {
James Feist8f2710a2018-05-09 17:18:55 -0700764 auto iface =
765 objServer.add_interface(boardName, boardField.key());
James Feist97a63f12018-05-17 13:50:57 -0700766 populateInterfaceFromJson(
767 systemConfiguration, jsonPointerPath + boardField.key(),
768 iface.get(), boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700769 }
770 }
James Feist97a63f12018-05-17 13:50:57 -0700771
James Feist1e3e6982018-08-03 16:09:28 -0700772 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800773 if (exposes == boardValues.end())
774 {
775 continue;
776 }
James Feist97a63f12018-05-17 13:50:57 -0700777 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700778 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700779
780 // store the board level pointer so we can modify it on the way down
781 std::string jsonPointerPathBoard = jsonPointerPath;
782 size_t exposesIndex = -1;
James Feist1b2e2242018-01-30 13:45:19 -0800783 for (auto &item : *exposes)
784 {
James Feist97a63f12018-05-17 13:50:57 -0700785 exposesIndex++;
786 jsonPointerPath = jsonPointerPathBoard;
787 jsonPointerPath += std::to_string(exposesIndex);
788
James Feistd63d18a2018-07-19 15:23:45 -0700789 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800790 if (findName == item.end())
791 {
792 std::cerr << "cannot find name in field " << item << "\n";
793 continue;
794 }
James Feist1e3e6982018-08-03 16:09:28 -0700795 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800796 // if status is not found it is assumed to be status = 'okay'
797 if (findStatus != item.end())
798 {
799 if (*findStatus == "disabled")
800 {
801 continue;
802 }
803 }
James Feistd63d18a2018-07-19 15:23:45 -0700804 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800805 std::string itemType;
806 if (findType != item.end())
807 {
808 itemType = findType->get<std::string>();
809 std::regex_replace(itemType.begin(), itemType.begin(),
810 itemType.end(), ILLEGAL_DBUS_REGEX, "_");
811 }
812 else
813 {
814 itemType = "unknown";
815 }
816 std::string itemName = findName->get<std::string>();
817 std::regex_replace(itemName.begin(), itemName.begin(),
818 itemName.end(), ILLEGAL_DBUS_REGEX, "_");
James Feist8f2710a2018-05-09 17:18:55 -0700819 auto itemIface = objServer.add_interface(
820 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800821 "xyz.openbmc_project.Configuration." + itemType);
822
James Feist97a63f12018-05-17 13:50:57 -0700823 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
824 itemIface.get(), item, objServer);
James Feist1b2e2242018-01-30 13:45:19 -0800825
James Feist9eb0b582018-04-27 12:15:46 -0700826 for (auto &objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800827 {
James Feist97a63f12018-05-17 13:50:57 -0700828 jsonPointerPath = jsonPointerPathBoard +
829 std::to_string(exposesIndex) + "/" +
830 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -0800831 if (objectPair.value().type() ==
832 nlohmann::json::value_t::object)
833 {
James Feist8f2710a2018-05-09 17:18:55 -0700834 auto objectIface = objServer.add_interface(
835 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800836 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -0700837 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -0700838
839 populateInterfaceFromJson(
840 systemConfiguration, jsonPointerPath, objectIface.get(),
841 objectPair.value(), objServer);
James Feist1b2e2242018-01-30 13:45:19 -0800842 }
843 else if (objectPair.value().type() ==
844 nlohmann::json::value_t::array)
845 {
846 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -0700847 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -0800848 {
James Feist8f2710a2018-05-09 17:18:55 -0700849 continue;
850 }
851 bool isLegal = true;
852 auto type = objectPair.value()[0].type();
853 if (type != nlohmann::json::value_t::object)
854 {
855 continue;
856 }
857
858 // verify legal json
859 for (const auto &arrayItem : objectPair.value())
860 {
861 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800862 {
James Feist8f2710a2018-05-09 17:18:55 -0700863 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800864 break;
865 }
James Feist8f2710a2018-05-09 17:18:55 -0700866 }
867 if (!isLegal)
868 {
869 std::cerr << "dbus format error" << objectPair.value()
870 << "\n";
871 break;
872 }
873
874 for (auto &arrayItem : objectPair.value())
875 {
James Feistbb43d022018-06-12 15:44:33 -0700876 // limit what interfaces accept set for safety
877 auto permission =
878 std::find(SETTABLE_INTERFACES.begin(),
879 SETTABLE_INTERFACES.end(),
880 objectPair.key()) !=
881 SETTABLE_INTERFACES.end()
882 ? sdbusplus::asio::PropertyPermission::readWrite
883 : sdbusplus::asio::PropertyPermission::readOnly;
James Feist97a63f12018-05-17 13:50:57 -0700884
James Feist8f2710a2018-05-09 17:18:55 -0700885 auto objectIface = objServer.add_interface(
886 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800887 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -0700888 "." + objectPair.key() + std::to_string(index));
889 populateInterfaceFromJson(systemConfiguration,
890 jsonPointerPath + "/" +
891 std::to_string(index),
892 objectIface.get(), arrayItem,
893 objServer, permission);
894 index++;
James Feist1b2e2242018-01-30 13:45:19 -0800895 }
896 }
897 }
898 }
899 }
900}
901
902// finds the template character (currently set to $) and replaces the value with
903// the field found in a dbus object i.e. $ADDRESS would get populated with the
904// ADDRESS field from a object on dbus
905void templateCharReplace(
906 nlohmann::json::iterator &keyPair,
James Feist8f2710a2018-05-09 17:18:55 -0700907 const boost::container::flat_map<std::string, BasicVariantType>
James Feist1b2e2242018-01-30 13:45:19 -0800908 &foundDevice,
909 size_t &foundDeviceIdx)
910{
James Feist11be6672018-04-06 14:05:32 -0700911 if (keyPair.value().type() == nlohmann::json::value_t::object)
912 {
913 for (auto nextLayer = keyPair.value().begin();
914 nextLayer != keyPair.value().end(); nextLayer++)
915 {
916 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
917 }
918 return;
919 }
920 else if (keyPair.value().type() != nlohmann::json::value_t::string)
James Feist1b2e2242018-01-30 13:45:19 -0800921 {
922 return;
923 }
924
925 std::string value = keyPair.value();
926 if (value.find(TEMPLATE_CHAR) != std::string::npos)
927 {
928 std::string templateValue = value;
929
930 templateValue.erase(0, 1); // remove template character
931
932 // special case index
933 if ("index" == templateValue)
934 {
935 keyPair.value() = foundDeviceIdx;
936 }
937 else
938 {
James Feist13b86d62018-05-29 11:24:35 -0700939 bool found = false;
James Feist1b2e2242018-01-30 13:45:19 -0800940 for (auto &foundDevicePair : foundDevice)
941 {
942 if (boost::iequals(foundDevicePair.first, templateValue))
943 {
James Feist13b86d62018-05-29 11:24:35 -0700944 mapbox::util::apply_visitor(
945 [&](auto &&val) { keyPair.value() = val; },
946 foundDevicePair.second);
947 found = true;
James Feist1b2e2242018-01-30 13:45:19 -0800948 break;
949 }
950 }
James Feist13b86d62018-05-29 11:24:35 -0700951 if (!found)
James Feist1b2e2242018-01-30 13:45:19 -0800952 {
953 std::cerr << "could not find symbol " << templateValue << "\n";
954 }
James Feist1b2e2242018-01-30 13:45:19 -0800955 }
956 }
957}
958
James Feist8f2710a2018-05-09 17:18:55 -0700959// reads json files out of the filesystem
960bool findJsonFiles(std::list<nlohmann::json> &configurations)
James Feist3cb5fec2018-01-23 14:41:51 -0800961{
962 // find configuration files
963 std::vector<fs::path> jsonPaths;
James Feistb4383f42018-08-06 16:54:10 -0700964 if (!find_files(fs::path(configurationDirectory), R"(.*\.json)", jsonPaths,
965 0))
James Feist3cb5fec2018-01-23 14:41:51 -0800966 {
967 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -0700968 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -0800969 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800970 }
James Feistb4383f42018-08-06 16:54:10 -0700971
972 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
973 globalSchema);
974 if (!schemaStream.good())
975 {
976 std::cerr
977 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
978 std::exit(EXIT_FAILURE);
979 }
980 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
981 if (schema.is_discarded())
982 {
983 std::cerr
984 << "Illegal schema file detected, cannot validate JSON, exiting\n";
985 std::exit(EXIT_FAILURE);
986 }
987
James Feist3cb5fec2018-01-23 14:41:51 -0800988 for (auto &jsonPath : jsonPaths)
989 {
990 std::ifstream jsonStream(jsonPath.c_str());
991 if (!jsonStream.good())
992 {
993 std::cerr << "unable to open " << jsonPath.string() << "\n";
994 continue;
995 }
996 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
997 if (data.is_discarded())
998 {
999 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1000 continue;
1001 }
James Feistb4383f42018-08-06 16:54:10 -07001002 if (!validateJson(schema, data))
1003 {
1004 std::cerr << "Error validating " << jsonPath.string() << "\n";
1005 continue;
1006 }
1007
James Feist3cb5fec2018-01-23 14:41:51 -08001008 if (data.type() == nlohmann::json::value_t::array)
1009 {
1010 for (auto &d : data)
1011 {
1012 configurations.emplace_back(d);
1013 }
1014 }
1015 else
1016 {
1017 configurations.emplace_back(data);
1018 }
1019 }
James Feist75fdeeb2018-02-20 14:26:16 -08001020}
James Feist3cb5fec2018-01-23 14:41:51 -08001021
James Feist8f2710a2018-05-09 17:18:55 -07001022struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001023{
James Feist75fdeeb2018-02-20 14:26:16 -08001024
James Feist8f2710a2018-05-09 17:18:55 -07001025 PerformScan(nlohmann::json &systemConfiguration,
1026 std::list<nlohmann::json> &configurations,
1027 std::function<void(void)> &&callback) :
1028 _systemConfiguration(systemConfiguration),
1029 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001030 {
James Feist8f2710a2018-05-09 17:18:55 -07001031 }
1032 void run()
1033 {
1034 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001035 {
James Feist1e3e6982018-08-03 16:09:28 -07001036 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001037 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001038
James Feist1b2e2242018-01-30 13:45:19 -08001039 nlohmann::json probeCommand;
1040 // check for poorly formatted fields, probe must be an array
1041 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001042 {
1043 std::cerr << "configuration file missing probe:\n " << *it
1044 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001045 it = _configurations.erase(it);
1046 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001047 }
James Feist1b2e2242018-01-30 13:45:19 -08001048 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001049 {
1050 probeCommand = nlohmann::json::array();
1051 probeCommand.push_back(*findProbe);
1052 }
1053 else
1054 {
1055 probeCommand = *findProbe;
1056 }
James Feist1b2e2242018-01-30 13:45:19 -08001057
1058 if (findName == it->end())
1059 {
1060 std::cerr << "configuration file missing name:\n " << *it
1061 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001062 it = _configurations.erase(it);
1063 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001064 }
James Feist8f2710a2018-05-09 17:18:55 -07001065 std::string name = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001066
James Feist8f2710a2018-05-09 17:18:55 -07001067 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(), name) !=
1068 PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001069 {
James Feist8f2710a2018-05-09 17:18:55 -07001070 it = _configurations.erase(it);
1071 continue;
1072 }
1073 nlohmann::json *record = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001074
James Feist8f2710a2018-05-09 17:18:55 -07001075 // store reference to this to children to makes sure we don't get
1076 // destroyed too early
1077 auto thisRef = shared_from_this();
1078 auto p = std::make_shared<PerformProbe>(
1079 probeCommand,
1080 [&, record, name,
1081 thisRef](std::vector<boost::container::flat_map<
1082 std::string, BasicVariantType>> &foundDevices) {
1083 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001084
James Feist8f2710a2018-05-09 17:18:55 -07001085 PASSED_PROBES.push_back(name);
1086 size_t foundDeviceIdx = 0;
1087
James Feistbe5425f2018-06-08 10:30:55 -07001088 // insert into configuration temporarly to be able to
1089 // reference ourselves
1090 _systemConfiguration[name] = *record;
1091
James Feist8f2710a2018-05-09 17:18:55 -07001092 for (auto &foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001093 {
James Feist8f2710a2018-05-09 17:18:55 -07001094 for (auto keyPair = record->begin();
1095 keyPair != record->end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001096 {
James Feist1b2e2242018-01-30 13:45:19 -08001097 templateCharReplace(keyPair, foundDevice,
1098 foundDeviceIdx);
James Feist8f2710a2018-05-09 17:18:55 -07001099 }
James Feist1e3e6982018-08-03 16:09:28 -07001100 auto findExpose = record->find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001101 if (findExpose == record->end())
1102 {
1103 continue;
1104 }
1105 for (auto &expose : *findExpose)
1106 {
1107 for (auto keyPair = expose.begin();
1108 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001109 {
James Feist1b2e2242018-01-30 13:45:19 -08001110
James Feist8f2710a2018-05-09 17:18:55 -07001111 // fill in template characters with devices
1112 // found
1113 templateCharReplace(keyPair, foundDevice,
1114 foundDeviceIdx);
1115 // special case bind
James Feist1e3e6982018-08-03 16:09:28 -07001116 if (boost::starts_with(keyPair.key(), "Bind"))
James Feist8f2710a2018-05-09 17:18:55 -07001117 {
1118 if (keyPair.value().type() !=
1119 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001120 {
James Feist8f2710a2018-05-09 17:18:55 -07001121 std::cerr << "bind_ value must be of "
1122 "type string "
1123 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001124 continue;
1125 }
James Feist8f2710a2018-05-09 17:18:55 -07001126 bool foundBind = false;
1127 std::string bind = keyPair.key().substr(
James Feist1e3e6982018-08-03 16:09:28 -07001128 sizeof("Bind") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001129
James Feist8f2710a2018-05-09 17:18:55 -07001130 for (auto &configurationPair :
1131 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001132 {
James Feist1b2e2242018-01-30 13:45:19 -08001133
James Feist8f2710a2018-05-09 17:18:55 -07001134 auto configListFind =
1135 configurationPair.value().find(
James Feist1e3e6982018-08-03 16:09:28 -07001136 "Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001137
1138 if (configListFind ==
1139 configurationPair.value()
1140 .end() ||
1141 configListFind->type() !=
1142 nlohmann::json::value_t::array)
1143 {
1144 continue;
1145 }
1146 for (auto &exposedObject :
1147 *configListFind)
1148 {
1149 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001150 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001151 if (boost::iequals(
1152 foundObjectName,
1153 keyPair.value()
1154 .get<std::string>()))
1155 {
James Feist1e3e6982018-08-03 16:09:28 -07001156 exposedObject["Status"] =
James Feist8f2710a2018-05-09 17:18:55 -07001157 "okay";
1158 expose[bind] = exposedObject;
1159
1160 foundBind = true;
1161 break;
1162 }
1163 }
1164 if (foundBind)
1165 {
James Feist3cb5fec2018-01-23 14:41:51 -08001166 break;
1167 }
1168 }
James Feist8f2710a2018-05-09 17:18:55 -07001169 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001170 {
James Feist8f2710a2018-05-09 17:18:55 -07001171 std::cerr << "configuration file "
1172 "dependency error, "
1173 "could not find bind "
1174 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001175 }
1176 }
1177 }
1178 }
1179 }
James Feistbe5425f2018-06-08 10:30:55 -07001180 // overwrite ourselves with cleaned up version
James Feist8f2710a2018-05-09 17:18:55 -07001181 _systemConfiguration[name] = *record;
1182 });
1183 p->run();
1184 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001185 }
1186 }
James Feist75fdeeb2018-02-20 14:26:16 -08001187
James Feist8f2710a2018-05-09 17:18:55 -07001188 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001189 {
James Feist8f2710a2018-05-09 17:18:55 -07001190 if (_passed)
1191 {
1192 auto nextScan = std::make_shared<PerformScan>(
1193 _systemConfiguration, _configurations, std::move(_callback));
1194 nextScan->run();
1195 }
1196 else
1197 {
1198 _callback();
1199 }
1200 }
1201 nlohmann::json &_systemConfiguration;
1202 std::list<nlohmann::json> _configurations;
1203 std::function<void(void)> _callback;
1204 std::vector<std::shared_ptr<PerformProbe>> _probes;
1205 bool _passed = false;
1206};
James Feistc95cb142018-02-26 10:41:42 -08001207
James Feist8f2710a2018-05-09 17:18:55 -07001208// main properties changed entry
1209void propertiesChangedCallback(
1210 boost::asio::io_service &io,
1211 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1212 nlohmann::json &systemConfiguration,
1213 sdbusplus::asio::object_server &objServer)
1214{
1215 static boost::asio::deadline_timer timer(io);
1216 timer.expires_from_now(boost::posix_time::seconds(1));
1217
1218 // setup an async wait as we normally get flooded with new requests
1219 timer.async_wait([&](const boost::system::error_code &ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001220 if (ec == boost::asio::error::operation_aborted)
1221 {
1222 // we were cancelled
1223 return;
1224 }
1225 else if (ec)
1226 {
1227 std::cerr << "async wait error " << ec << "\n";
1228 return;
1229 }
1230
1231 nlohmann::json oldConfiguration = systemConfiguration;
1232 DBUS_PROBE_OBJECTS.clear();
1233
1234 std::list<nlohmann::json> configurations;
1235 if (!findJsonFiles(configurations))
1236 {
1237 std::cerr << "cannot find json files\n";
1238 return;
1239 }
1240
1241 auto perfScan = std::make_shared<PerformScan>(
1242 systemConfiguration, configurations, [&, oldConfiguration]() {
1243 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001244 for (auto it = newConfiguration.begin();
1245 it != newConfiguration.end();)
1246 {
1247 auto findKey = oldConfiguration.find(it.key());
1248 if (findKey != oldConfiguration.end())
1249 {
1250 it = newConfiguration.erase(it);
1251 }
1252 else
1253 {
1254 it++;
1255 }
1256 }
James Feist8f2710a2018-05-09 17:18:55 -07001257 registerCallbacks(io, dbusMatches, systemConfiguration,
1258 objServer);
1259 io.post([&, newConfiguration]() {
1260 // todo: for now, only add new configurations,
1261 // unload to come later unloadOverlays();
1262 loadOverlays(newConfiguration);
James Feistbb43d022018-06-12 15:44:33 -07001263 io.post([&]() {
1264 if (!writeJsonFiles(systemConfiguration))
1265 {
1266 std::cerr << "Error writing json files\n";
1267 }
1268 });
James Feist8f2710a2018-05-09 17:18:55 -07001269 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001270 postToDbus(newConfiguration, systemConfiguration,
1271 objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001272 });
1273 });
1274 });
1275 perfScan->run();
1276 });
James Feist75fdeeb2018-02-20 14:26:16 -08001277}
1278
James Feist8f2710a2018-05-09 17:18:55 -07001279void registerCallbacks(boost::asio::io_service &io,
1280 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1281 nlohmann::json &systemConfiguration,
1282 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001283{
1284 static boost::container::flat_set<std::string> watchedObjects;
1285
1286 for (const auto &objectMap : DBUS_PROBE_OBJECTS)
1287 {
1288 auto findObject = watchedObjects.find(objectMap.first);
1289 if (findObject != watchedObjects.end())
1290 {
1291 continue;
1292 }
James Feist8f2710a2018-05-09 17:18:55 -07001293 std::function<void(sdbusplus::message::message & message)>
1294 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001295
James Feist8f2710a2018-05-09 17:18:55 -07001296 [&](sdbusplus::message::message &) {
1297 propertiesChangedCallback(io, dbusMatches,
1298 systemConfiguration, objServer);
1299 };
1300
1301 sdbusplus::bus::match::match match(
1302 static_cast<sdbusplus::bus::bus &>(*SYSTEM_BUS),
1303 "type='signal',member='PropertiesChanged',arg0='" +
1304 objectMap.first + "'",
1305 eventHandler);
1306 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001307 }
1308}
1309
1310int main(int argc, char **argv)
1311{
1312 // setup connection to dbus
1313 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001314 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001315 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001316
James Feist8f2710a2018-05-09 17:18:55 -07001317 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001318
James Feist8f2710a2018-05-09 17:18:55 -07001319 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1320 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1321 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001322
James Feist8f2710a2018-05-09 17:18:55 -07001323 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1324 objServer.add_interface("/xyz/openbmc_project/inventory",
1325 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001326
1327 // to keep reference to the match / filter objects so they don't get
1328 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001329 std::vector<sdbusplus::bus::match::match> dbusMatches;
1330
1331 nlohmann::json systemConfiguration = nlohmann::json::object();
1332
1333 inventoryIface->register_method(
1334 "Notify", [](const boost::container::flat_map<
1335 std::string,
1336 boost::container::flat_map<std::string, BasicVariantType>>
1337 &object) { return; });
1338 inventoryIface->initialize();
1339
1340 io.post([&]() {
1341 unloadAllOverlays();
1342 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1343 objServer);
1344 });
James Feist4131aea2018-03-09 09:47:30 -08001345
James Feistfd1264a2018-05-03 12:10:00 -07001346 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001347 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1348 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001349 });
James Feist8f2710a2018-05-09 17:18:55 -07001350 entityIface->initialize();
1351
James Feist1b2e2242018-01-30 13:45:19 -08001352 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001353
1354 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001355}