blob: c93fb605a13ea800d871fba68d828aa0be535e6d [file] [log] [blame]
James Feist3cb5fec2018-01-23 14:41:51 -08001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include <Utils.hpp>
James Feistc95cb142018-02-26 10:41:42 -080018#include <Overlay.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080019#include <nlohmann/json.hpp>
20#include <fstream>
21#include <regex>
James Feist8f2710a2018-05-09 17:18:55 -070022#include <iostream>
23#include <sdbusplus/asio/connection.hpp>
24#include <sdbusplus/asio/object_server.hpp>
James Feist11be6672018-04-06 14:05:32 -070025#include <boost/algorithm/string/case_conv.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080026#include <boost/algorithm/string/predicate.hpp>
27#include <boost/algorithm/string/replace.hpp>
28#include <boost/variant/apply_visitor.hpp>
29#include <boost/lexical_cast.hpp>
30#include <boost/container/flat_map.hpp>
31#include <boost/container/flat_set.hpp>
32#include <VariantVisitors.hpp>
James Feist7b7e4e82018-01-24 14:56:00 -080033#include <experimental/filesystem>
James Feist3cb5fec2018-01-23 14:41:51 -080034
35constexpr const char *OUTPUT_DIR = "/var/configuration/";
36constexpr const char *CONFIGURATION_DIR = "/usr/share/configurations";
37constexpr const char *TEMPLATE_CHAR = "$";
James Feistc95cb142018-02-26 10:41:42 -080038constexpr const size_t PROPERTIES_CHANGED_UNTIL_FLUSH_COUNT = 20;
James Feist8f2710a2018-05-09 17:18:55 -070039constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist4131aea2018-03-09 09:47:30 -080040constexpr const size_t SLEEP_AFTER_PROPERTIES_CHANGE_SECONDS = 5;
James Feist3cb5fec2018-01-23 14:41:51 -080041
42namespace fs = std::experimental::filesystem;
43struct cmp_str
44{
45 bool operator()(const char *a, const char *b) const
46 {
47 return std::strcmp(a, b) < 0;
48 }
49};
50
James Feist8f2710a2018-05-09 17:18:55 -070051struct PerformProbe;
52
James Feist3cb5fec2018-01-23 14:41:51 -080053// underscore T for collison with dbus c api
54enum class probe_type_codes
55{
56 FALSE_T,
57 TRUE_T,
58 AND,
59 OR,
James Feist6bd2a022018-03-13 12:30:58 -070060 FOUND,
61 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080062};
63const static boost::container::flat_map<const char *, probe_type_codes, cmp_str>
64 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
65 {"TRUE", probe_type_codes::TRUE_T},
66 {"AND", probe_type_codes::AND},
67 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070068 {"FOUND", probe_type_codes::FOUND},
69 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080070
James Feist97a63f12018-05-17 13:50:57 -070071static constexpr std::array<const char *, 1> SETTABLE_INTERFACES = {
72 "thresholds"};
73
James Feist8f2710a2018-05-09 17:18:55 -070074using BasicVariantType =
75 sdbusplus::message::variant<std::string, int64_t, uint64_t, double, int32_t,
76 uint32_t, int16_t, uint16_t, uint8_t, bool>;
77
James Feist3cb5fec2018-01-23 14:41:51 -080078using GetSubTreeType = std::vector<
79 std::pair<std::string,
80 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
81
82using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070083 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080084 boost::container::flat_map<
85 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070086 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080087
88boost::container::flat_map<
89 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070090 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -080091 DBUS_PROBE_OBJECTS;
92std::vector<std::string> PASSED_PROBES;
93
94// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -070095std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist3cb5fec2018-01-23 14:41:51 -080096
James Feista6750242018-07-16 14:12:27 -070097const std::regex ILLEGAL_DBUS_REGEX("[^A-Za-z0-9_.]");
James Feist1b2e2242018-01-30 13:45:19 -080098
James Feist8f2710a2018-05-09 17:18:55 -070099void registerCallbacks(boost::asio::io_service &io,
100 std::vector<sdbusplus::bus::match::match> &dbusMatches,
101 nlohmann::json &systemConfiguration,
102 sdbusplus::asio::object_server &objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800103
James Feist3cb5fec2018-01-23 14:41:51 -0800104// calls the mapper to find all exposed objects of an interface type
105// and creates a vector<flat_map> that contains all the key value pairs
106// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700107void findDbusObjects(std::shared_ptr<PerformProbe> probe,
108 std::shared_ptr<sdbusplus::asio::connection> connection,
109 std::string &interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800110{
James Feist8f2710a2018-05-09 17:18:55 -0700111
112 // store reference to pending callbacks so we don't overwhelm services
113 static boost::container::flat_map<
114 std::string, std::vector<std::shared_ptr<PerformProbe>>>
115 pendingProbes;
116
117 if (DBUS_PROBE_OBJECTS[interface].size())
118 {
119 return;
120 }
121
122 // add shared_ptr to vector of Probes waiting for callback from a specific
123 // interface to keep alive while waiting for response
124 std::array<const char *, 1> objects = {interface.c_str()};
125 std::vector<std::shared_ptr<PerformProbe>> &pending =
126 pendingProbes[interface];
127 auto iter = pending.emplace(pending.end(), probe);
128 // only allow first call to run to not overwhelm processes
129 if (iter != pending.begin())
130 {
131 return;
132 }
133
James Feist3cb5fec2018-01-23 14:41:51 -0800134 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700135 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700136 [connection, interface, probe](boost::system::error_code &ec,
137 const GetSubTreeType &interfaceSubtree) {
138 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700139 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700140 {
James Feist0de40152018-07-25 11:56:12 -0700141 pendingProbes[interface].clear();
142 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700143 {
James Feist0de40152018-07-25 11:56:12 -0700144 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700145 }
James Feist0de40152018-07-25 11:56:12 -0700146 std::cerr << "Error communicating to mapper.\n";
147
148 // if we can't communicate to the mapper something is very wrong
149 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700150 }
James Feist8f2710a2018-05-09 17:18:55 -0700151 else
James Feist3cb5fec2018-01-23 14:41:51 -0800152 {
James Feist8f2710a2018-05-09 17:18:55 -0700153 for (auto &object : interfaceSubtree)
154 {
155 for (auto &connPair : object.second)
156 {
157 auto insertPair =
158 interfaceConnections.insert(connPair.first);
159 }
160 }
James Feist3cb5fec2018-01-23 14:41:51 -0800161 }
James Feist8f2710a2018-05-09 17:18:55 -0700162 // get managed objects for all interfaces
163 for (const auto &conn : interfaceConnections)
164 {
165 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700166 [conn,
James Feist8f2710a2018-05-09 17:18:55 -0700167 interface](boost::system::error_code &ec,
168 const ManagedObjectType &managedInterface) {
169 if (ec)
170 {
171 std::cerr
172 << "error getting managed object for device "
173 << conn << "\n";
174 pendingProbes[interface].clear();
175 return;
176 }
177 for (auto &interfaceManagedObj : managedInterface)
178 {
179 auto ifaceObjFind =
180 interfaceManagedObj.second.find(interface);
181 if (ifaceObjFind !=
182 interfaceManagedObj.second.end())
183 {
184 std::vector<boost::container::flat_map<
185 std::string, BasicVariantType>>
186 &dbusObject = DBUS_PROBE_OBJECTS[interface];
187 dbusObject.emplace_back(ifaceObjFind->second);
188 }
189 }
190 pendingProbes[interface].clear();
191 },
192 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
193 "GetManagedObjects");
194 }
195 },
196 "xyz.openbmc_project.ObjectMapper",
197 "/xyz/openbmc_project/object_mapper",
198 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "", MAX_MAPPER_DEPTH,
199 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800200}
James Feist8f2710a2018-05-09 17:18:55 -0700201// probes dbus interface dictionary for a key with a value that matches a regex
James Feist3cb5fec2018-01-23 14:41:51 -0800202bool probeDbus(
203 const std::string &interface,
204 const std::map<std::string, nlohmann::json> &matches,
James Feist8f2710a2018-05-09 17:18:55 -0700205 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
James Feist3cb5fec2018-01-23 14:41:51 -0800206 &devices,
207 bool &foundProbe)
208{
James Feist8f2710a2018-05-09 17:18:55 -0700209 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
210 &dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800211 if (dbusObject.empty())
212 {
James Feist8f2710a2018-05-09 17:18:55 -0700213 foundProbe = false;
214 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800215 }
216 foundProbe = true;
217
218 bool foundMatch = false;
219 for (auto &device : dbusObject)
220 {
221 bool deviceMatches = true;
222 for (auto &match : matches)
223 {
224 auto deviceValue = device.find(match.first);
225 if (deviceValue != device.end())
226 {
227 switch (match.second.type())
228 {
James Feist9eb0b582018-04-27 12:15:46 -0700229 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800230 {
James Feist9eb0b582018-04-27 12:15:46 -0700231 std::regex search(match.second.get<std::string>());
232 std::smatch match;
233
234 // convert value to string respresentation
James Feist8f2710a2018-05-09 17:18:55 -0700235 std::string probeValue = mapbox::util::apply_visitor(
236 VariantToStringVisitor(), deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700237 if (!std::regex_search(probeValue, match, search))
238 {
239 deviceMatches = false;
240 break;
241 }
James Feist3cb5fec2018-01-23 14:41:51 -0800242 break;
243 }
James Feist9eb0b582018-04-27 12:15:46 -0700244 case nlohmann::json::value_t::boolean:
245 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800246 {
James Feist8f2710a2018-05-09 17:18:55 -0700247 unsigned int probeValue = mapbox::util::apply_visitor(
James Feist9eb0b582018-04-27 12:15:46 -0700248 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800249
James Feist9eb0b582018-04-27 12:15:46 -0700250 if (probeValue != match.second.get<unsigned int>())
251 {
252 deviceMatches = false;
253 }
254 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800255 }
James Feist9eb0b582018-04-27 12:15:46 -0700256 case nlohmann::json::value_t::number_integer:
257 {
James Feist8f2710a2018-05-09 17:18:55 -0700258 int probeValue = mapbox::util::apply_visitor(
James Feist9eb0b582018-04-27 12:15:46 -0700259 VariantToIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800260
James Feist9eb0b582018-04-27 12:15:46 -0700261 if (probeValue != match.second.get<int>())
262 {
263 deviceMatches = false;
264 }
265 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800266 }
James Feist9eb0b582018-04-27 12:15:46 -0700267 case nlohmann::json::value_t::number_float:
268 {
James Feist8f2710a2018-05-09 17:18:55 -0700269 float probeValue = mapbox::util::apply_visitor(
James Feist9eb0b582018-04-27 12:15:46 -0700270 VariantToFloatVisitor(), deviceValue->second);
271
272 if (probeValue != match.second.get<float>())
273 {
274 deviceMatches = false;
275 }
276 break;
277 }
James Feist3cb5fec2018-01-23 14:41:51 -0800278 }
279 }
280 else
281 {
282 deviceMatches = false;
283 break;
284 }
285 }
286 if (deviceMatches)
287 {
288 devices.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700289 boost::container::flat_map<std::string, BasicVariantType>(
James Feist3cb5fec2018-01-23 14:41:51 -0800290 device));
291 foundMatch = true;
292 deviceMatches = false; // for next iteration
293 }
294 }
295 return foundMatch;
296}
297
298// default probe entry point, iterates a list looking for specific types to
299// call specific probe functions
300bool probe(
James Feist8f2710a2018-05-09 17:18:55 -0700301 const std::vector<std::string> &probeCommand,
302 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
James Feist3cb5fec2018-01-23 14:41:51 -0800303 &foundDevs)
304{
305 const static std::regex command(R"(\((.*)\))");
306 std::smatch match;
307 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700308 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800309 bool cur = true;
310 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
311
312 for (auto &probe : probeCommand)
313 {
314 bool foundProbe = false;
315 boost::container::flat_map<const char *, probe_type_codes,
316 cmp_str>::const_iterator probeType;
317
318 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
319 probeType++)
320 {
321 if (probe.find(probeType->first) != std::string::npos)
322 {
323 foundProbe = true;
324 break;
325 }
326 }
327 if (foundProbe)
328 {
329 switch (probeType->second)
330 {
James Feist9eb0b582018-04-27 12:15:46 -0700331 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800332 {
James Feist8f2710a2018-05-09 17:18:55 -0700333 return false;
James Feist9eb0b582018-04-27 12:15:46 -0700334 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800335 }
James Feist9eb0b582018-04-27 12:15:46 -0700336 case probe_type_codes::TRUE_T:
337 {
James Feist8f2710a2018-05-09 17:18:55 -0700338 return true;
James Feist9eb0b582018-04-27 12:15:46 -0700339 break;
340 }
341 case probe_type_codes::MATCH_ONE:
342 {
343 // set current value to last, this probe type shouldn't
344 // affect the outcome
345 cur = ret;
346 matchOne = true;
347 break;
348 }
349 /*case probe_type_codes::AND:
350 break;
351 case probe_type_codes::OR:
352 break;
353 // these are no-ops until the last command switch
354 */
355 case probe_type_codes::FOUND:
356 {
357 if (!std::regex_search(probe, match, command))
358 {
359 std::cerr << "found probe sytax error " << probe
360 << "\n";
361 return false;
362 }
363 std::string commandStr = *(match.begin() + 1);
364 boost::replace_all(commandStr, "'", "");
365 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
366 commandStr) != PASSED_PROBES.end());
367 break;
368 }
James Feist3cb5fec2018-01-23 14:41:51 -0800369 }
370 }
371 // look on dbus for object
372 else
373 {
374 if (!std::regex_search(probe, match, command))
375 {
376 std::cerr << "dbus probe sytax error " << probe << "\n";
377 return false;
378 }
379 std::string commandStr = *(match.begin() + 1);
380 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700381 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800382 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800383 auto json = nlohmann::json::parse(commandStr, nullptr, false);
384 if (json.is_discarded())
385 {
386 std::cerr << "dbus command sytax error " << commandStr << "\n";
387 return false;
388 }
389 // we can match any (string, variant) property. (string, string)
390 // does a regex
391 std::map<std::string, nlohmann::json> dbusProbeMap =
392 json.get<std::map<std::string, nlohmann::json>>();
393 auto findStart = probe.find("(");
394 if (findStart == std::string::npos)
395 {
396 return false;
397 }
398 std::string probeInterface = probe.substr(0, findStart);
399 cur =
400 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
401 }
402
403 // some functions like AND and OR only take affect after the
404 // fact
405 switch (lastCommand)
406 {
James Feist9eb0b582018-04-27 12:15:46 -0700407 case probe_type_codes::AND:
408 ret = cur && ret;
409 break;
410 case probe_type_codes::OR:
411 ret = cur || ret;
412 break;
413 default:
414 ret = cur;
415 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800416 }
417 lastCommand = probeType != PROBE_TYPES.end()
418 ? probeType->second
419 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800420 }
421
422 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800423 if (ret && foundDevs.size() == 0)
424 {
425 foundDevs.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700426 boost::container::flat_map<std::string, BasicVariantType>());
James Feist3cb5fec2018-01-23 14:41:51 -0800427 }
James Feist6bd2a022018-03-13 12:30:58 -0700428 if (matchOne && foundDevs.size() > 1)
429 {
430 foundDevs.erase(foundDevs.begin() + 1, foundDevs.end());
431 }
James Feist3cb5fec2018-01-23 14:41:51 -0800432 return ret;
433}
James Feist8f2710a2018-05-09 17:18:55 -0700434// this class finds the needed dbus fields and on destruction runs the probe
435struct PerformProbe : std::enable_shared_from_this<PerformProbe>
436{
James Feist3cb5fec2018-01-23 14:41:51 -0800437
James Feist8f2710a2018-05-09 17:18:55 -0700438 PerformProbe(
439 const std::vector<std::string> &probeCommand,
440 std::function<void(std::vector<boost::container::flat_map<
441 std::string, BasicVariantType>> &)> &&callback) :
442 _probeCommand(probeCommand),
443 _callback(std::move(callback))
444 {
445 }
446 ~PerformProbe()
447 {
448 if (probe(_probeCommand, _foundDevs))
449 {
450 _callback(_foundDevs);
451 }
452 }
453 void run()
454 {
455 // parse out dbus probes by discarding other probe types
456 boost::container::flat_map<const char *, probe_type_codes,
457 cmp_str>::const_iterator probeType;
458
459 std::vector<std::string> dbusProbes;
460 for (std::string &probe : _probeCommand)
461 {
462 bool found = false;
463 boost::container::flat_map<const char *, probe_type_codes,
464 cmp_str>::const_iterator probeType;
465 for (probeType = PROBE_TYPES.begin();
466 probeType != PROBE_TYPES.end(); probeType++)
467 {
468 if (probe.find(probeType->first) != std::string::npos)
469 {
470 found = true;
471 break;
472 }
473 }
474 if (found)
475 {
476 continue;
477 }
478 // syntax requires probe before first open brace
479 auto findStart = probe.find("(");
480 std::string interface = probe.substr(0, findStart);
481
482 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
483 }
484 }
485 std::vector<std::string> _probeCommand;
486 std::function<void(
487 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
488 &)>
489 _callback;
490 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
491 _foundDevs;
492};
493
494// writes output files to persist data
James Feistbb43d022018-06-12 15:44:33 -0700495bool writeJsonFiles(const nlohmann::json &systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800496{
497 std::experimental::filesystem::create_directory(OUTPUT_DIR);
498 std::ofstream output(std::string(OUTPUT_DIR) + "system.json");
James Feistbb43d022018-06-12 15:44:33 -0700499 if (!output.good())
500 {
501 return false;
502 }
James Feist1b2e2242018-01-30 13:45:19 -0800503 output << systemConfiguration.dump(4);
504 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700505 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700506}
James Feist1b2e2242018-01-30 13:45:19 -0800507
James Feist8f2710a2018-05-09 17:18:55 -0700508// template function to add array as dbus property
509template <typename PropertyType>
510void addArrayToDbus(const std::string &name, const nlohmann::json &array,
James Feistbb43d022018-06-12 15:44:33 -0700511 sdbusplus::asio::dbus_interface *iface,
512 sdbusplus::asio::PropertyPermission permission)
James Feist8f2710a2018-05-09 17:18:55 -0700513{
514 std::vector<PropertyType> values;
515 for (const auto &property : array)
James Feist1b2e2242018-01-30 13:45:19 -0800516 {
James Feist8f2710a2018-05-09 17:18:55 -0700517 auto ptr = property.get_ptr<const PropertyType *>();
518 if (ptr != nullptr)
James Feist1b2e2242018-01-30 13:45:19 -0800519 {
James Feist8f2710a2018-05-09 17:18:55 -0700520 values.emplace_back(*ptr);
James Feist1b2e2242018-01-30 13:45:19 -0800521 }
522 }
James Feistbb43d022018-06-12 15:44:33 -0700523 // todo(james), currently there are no reason to persist arrays, get around
524 // to it if needed
525
526 iface->register_property(name, values, permission);
James Feist1b2e2242018-01-30 13:45:19 -0800527}
James Feist97a63f12018-05-17 13:50:57 -0700528
529template <typename JsonType>
James Feistbb43d022018-06-12 15:44:33 -0700530bool setJsonFromPointer(const std::string &ptrStr, const JsonType &value,
James Feist97a63f12018-05-17 13:50:57 -0700531 nlohmann::json &systemConfiguration)
532{
533 try
534 {
535 nlohmann::json::json_pointer ptr(ptrStr);
536 nlohmann::json &ref = systemConfiguration[ptr];
537 ref = value;
538 return true;
539 }
540 catch (const std::out_of_range)
541 {
542 return false;
543 }
544}
James Feistbb43d022018-06-12 15:44:33 -0700545
546template <typename PropertyType>
547void addProperty(const std::string &propertyName, const PropertyType &value,
548 sdbusplus::asio::dbus_interface *iface,
549 nlohmann::json &systemConfiguration,
550 const std::string &jsonPointerString,
551 sdbusplus::asio::PropertyPermission permission)
552{
553 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
554 {
555 iface->register_property(propertyName, value);
556 return;
557 }
James Feist0de40152018-07-25 11:56:12 -0700558 iface->register_property(propertyName, value, [
559 &systemConfiguration, jsonPointerString{std::string(jsonPointerString)}
560 ](const PropertyType &newVal, PropertyType &val) {
561 val = newVal;
562 if (!setJsonFromPointer(jsonPointerString, val, systemConfiguration))
563 {
564 std::cerr << "error setting json field\n";
James Feistbb43d022018-06-12 15:44:33 -0700565 return -1;
James Feist0de40152018-07-25 11:56:12 -0700566 }
567 if (writeJsonFiles(systemConfiguration))
568 {
569 std::cerr << "error setting json file\n";
570 return 1;
571 }
572 return -1;
573 });
James Feistbb43d022018-06-12 15:44:33 -0700574}
575
James Feist1b2e2242018-01-30 13:45:19 -0800576// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700577void populateInterfaceFromJson(
578 nlohmann::json &systemConfiguration, const std::string &jsonPointerPath,
579 sdbusplus::asio::dbus_interface *iface, nlohmann::json &dict,
580 sdbusplus::asio::object_server &objServer,
581 sdbusplus::asio::PropertyPermission permission =
582 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800583{
James Feist9eb0b582018-04-27 12:15:46 -0700584 for (auto &dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800585 {
James Feist8f2710a2018-05-09 17:18:55 -0700586 auto type = dictPair.value().type();
587 bool array = false;
588 if (dictPair.value().type() == nlohmann::json::value_t::array)
589 {
590 array = true;
591 if (!dictPair.value().size())
592 {
593 continue;
594 }
595 type = dictPair.value()[0].type();
596 bool isLegal = true;
597 for (const auto &arrayItem : dictPair.value())
598 {
599 if (arrayItem.type() != type)
600 {
601 isLegal = false;
602 break;
603 }
604 }
605 if (!isLegal)
606 {
607 std::cerr << "dbus format error" << dictPair.value() << "\n";
608 continue;
609 }
610 if (type == nlohmann::json::value_t::object)
611 {
612 continue; // handled elsewhere
613 }
614 }
James Feist97a63f12018-05-17 13:50:57 -0700615 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700616 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
617 {
618 // all setable numbers are doubles as it is difficult to always
619 // create a configuration file with all whole numbers as decimals
620 // i.e. 1.0
621 if (dictPair.value().is_number())
622 {
623 type = nlohmann::json::value_t::number_float;
624 }
625 }
626
James Feist8f2710a2018-05-09 17:18:55 -0700627 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800628 {
James Feist9eb0b582018-04-27 12:15:46 -0700629 case (nlohmann::json::value_t::boolean):
630 {
James Feist8f2710a2018-05-09 17:18:55 -0700631 if (array)
632 {
633 // todo: array of bool isn't detected correctly by
634 // sdbusplus, change it to numbers
635 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700636 iface, permission);
James Feist8f2710a2018-05-09 17:18:55 -0700637 }
James Feistbb43d022018-06-12 15:44:33 -0700638
James Feist97a63f12018-05-17 13:50:57 -0700639 else
640 {
James Feistbb43d022018-06-12 15:44:33 -0700641 addProperty(dictPair.key(), dictPair.value().get<bool>(),
642 iface, systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700643 }
James Feist9eb0b582018-04-27 12:15:46 -0700644 break;
645 }
646 case (nlohmann::json::value_t::number_integer):
647 {
James Feist8f2710a2018-05-09 17:18:55 -0700648 if (array)
649 {
650 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700651 iface, permission);
James Feist97a63f12018-05-17 13:50:57 -0700652 }
653 else
654 {
James Feistbb43d022018-06-12 15:44:33 -0700655 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
656 iface, systemConfiguration, key,
657 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700658 }
James Feist9eb0b582018-04-27 12:15:46 -0700659 break;
660 }
661 case (nlohmann::json::value_t::number_unsigned):
662 {
James Feist8f2710a2018-05-09 17:18:55 -0700663 if (array)
664 {
665 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700666 iface, permission);
James Feist97a63f12018-05-17 13:50:57 -0700667 }
668 else
669 {
James Feistbb43d022018-06-12 15:44:33 -0700670 addProperty(dictPair.key(),
671 dictPair.value().get<uint64_t>(), iface,
672 systemConfiguration, key,
673 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700674 }
James Feist9eb0b582018-04-27 12:15:46 -0700675 break;
676 }
677 case (nlohmann::json::value_t::number_float):
678 {
James Feist8f2710a2018-05-09 17:18:55 -0700679 if (array)
680 {
681 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistbb43d022018-06-12 15:44:33 -0700682 iface, permission);
James Feist8f2710a2018-05-09 17:18:55 -0700683 }
James Feistbb43d022018-06-12 15:44:33 -0700684
James Feist97a63f12018-05-17 13:50:57 -0700685 else
686 {
James Feistbb43d022018-06-12 15:44:33 -0700687 addProperty(dictPair.key(), dictPair.value().get<double>(),
688 iface, systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700689 }
James Feist9eb0b582018-04-27 12:15:46 -0700690 break;
691 }
692 case (nlohmann::json::value_t::string):
693 {
James Feist8f2710a2018-05-09 17:18:55 -0700694 if (array)
695 {
James Feistbb43d022018-06-12 15:44:33 -0700696 addArrayToDbus<std::string>(
697 dictPair.key(), dictPair.value(), iface, permission);
James Feist97a63f12018-05-17 13:50:57 -0700698 }
699 else
700 {
James Feistbb43d022018-06-12 15:44:33 -0700701 addProperty(dictPair.key(),
702 dictPair.value().get<std::string>(), iface,
703 systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700704 }
James Feist9eb0b582018-04-27 12:15:46 -0700705 break;
706 }
James Feist1b2e2242018-01-30 13:45:19 -0800707 }
708 }
James Feist1b2e2242018-01-30 13:45:19 -0800709
James Feist8f2710a2018-05-09 17:18:55 -0700710 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800711}
712
James Feist97a63f12018-05-17 13:50:57 -0700713void postToDbus(const nlohmann::json &newConfiguration,
714 nlohmann::json &systemConfiguration,
James Feist8f2710a2018-05-09 17:18:55 -0700715 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800716
James Feist1b2e2242018-01-30 13:45:19 -0800717{
James Feist97a63f12018-05-17 13:50:57 -0700718 // iterate through boards
719 for (auto &boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800720 {
721 std::string boardKey = boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700722 std::vector<std::string> path;
723 std::string jsonPointerPath = "/" + boardKey;
724 // loop through newConfiguration, but use values from system
725 // configuration to be able to modify via dbus later
726 auto boardValues = systemConfiguration[boardKey];
James Feistd63d18a2018-07-19 15:23:45 -0700727 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800728 std::string boardType;
729 if (findBoardType != boardValues.end() &&
730 findBoardType->type() == nlohmann::json::value_t::string)
731 {
732 boardType = findBoardType->get<std::string>();
733 std::regex_replace(boardType.begin(), boardType.begin(),
734 boardType.end(), ILLEGAL_DBUS_REGEX, "_");
735 }
736 else
737 {
738 std::cerr << "Unable to find type for " << boardKey
739 << " reverting to Chassis.\n";
740 boardType = "Chassis";
741 }
James Feist11be6672018-04-06 14:05:32 -0700742 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800743
744 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
745 ILLEGAL_DBUS_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700746 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
747 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800748
James Feist8f2710a2018-05-09 17:18:55 -0700749 auto inventoryIface = objServer.add_interface(
750 boardName, "xyz.openbmc_project.Inventory.Item");
751 auto boardIface = objServer.add_interface(
752 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700753
James Feist97a63f12018-05-17 13:50:57 -0700754 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
755 boardIface.get(), boardValues, objServer);
756 jsonPointerPath += "/";
757 // iterate through board properties
James Feist9eb0b582018-04-27 12:15:46 -0700758 for (auto &boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700759 {
760 if (boardField.value().type() == nlohmann::json::value_t::object)
761 {
James Feist8f2710a2018-05-09 17:18:55 -0700762 auto iface =
763 objServer.add_interface(boardName, boardField.key());
James Feist97a63f12018-05-17 13:50:57 -0700764 populateInterfaceFromJson(
765 systemConfiguration, jsonPointerPath + boardField.key(),
766 iface.get(), boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700767 }
768 }
James Feist97a63f12018-05-17 13:50:57 -0700769
James Feist1b2e2242018-01-30 13:45:19 -0800770 auto exposes = boardValues.find("exposes");
771 if (exposes == boardValues.end())
772 {
773 continue;
774 }
James Feist97a63f12018-05-17 13:50:57 -0700775 // iterate through exposes
776 jsonPointerPath += "exposes/";
777
778 // store the board level pointer so we can modify it on the way down
779 std::string jsonPointerPathBoard = jsonPointerPath;
780 size_t exposesIndex = -1;
James Feist1b2e2242018-01-30 13:45:19 -0800781 for (auto &item : *exposes)
782 {
James Feist97a63f12018-05-17 13:50:57 -0700783 exposesIndex++;
784 jsonPointerPath = jsonPointerPathBoard;
785 jsonPointerPath += std::to_string(exposesIndex);
786
James Feistd63d18a2018-07-19 15:23:45 -0700787 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800788 if (findName == item.end())
789 {
790 std::cerr << "cannot find name in field " << item << "\n";
791 continue;
792 }
793 auto findStatus = item.find("status");
794 // if status is not found it is assumed to be status = 'okay'
795 if (findStatus != item.end())
796 {
797 if (*findStatus == "disabled")
798 {
799 continue;
800 }
801 }
James Feistd63d18a2018-07-19 15:23:45 -0700802 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800803 std::string itemType;
804 if (findType != item.end())
805 {
806 itemType = findType->get<std::string>();
807 std::regex_replace(itemType.begin(), itemType.begin(),
808 itemType.end(), ILLEGAL_DBUS_REGEX, "_");
809 }
810 else
811 {
812 itemType = "unknown";
813 }
814 std::string itemName = findName->get<std::string>();
815 std::regex_replace(itemName.begin(), itemName.begin(),
816 itemName.end(), ILLEGAL_DBUS_REGEX, "_");
James Feist8f2710a2018-05-09 17:18:55 -0700817 auto itemIface = objServer.add_interface(
818 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800819 "xyz.openbmc_project.Configuration." + itemType);
820
James Feist97a63f12018-05-17 13:50:57 -0700821 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
822 itemIface.get(), item, objServer);
James Feist1b2e2242018-01-30 13:45:19 -0800823
James Feist9eb0b582018-04-27 12:15:46 -0700824 for (auto &objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800825 {
James Feist97a63f12018-05-17 13:50:57 -0700826 jsonPointerPath = jsonPointerPathBoard +
827 std::to_string(exposesIndex) + "/" +
828 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -0800829 if (objectPair.value().type() ==
830 nlohmann::json::value_t::object)
831 {
James Feist8f2710a2018-05-09 17:18:55 -0700832 auto objectIface = objServer.add_interface(
833 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800834 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -0700835 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -0700836
837 populateInterfaceFromJson(
838 systemConfiguration, jsonPointerPath, objectIface.get(),
839 objectPair.value(), objServer);
James Feist1b2e2242018-01-30 13:45:19 -0800840 }
841 else if (objectPair.value().type() ==
842 nlohmann::json::value_t::array)
843 {
844 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -0700845 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -0800846 {
James Feist8f2710a2018-05-09 17:18:55 -0700847 continue;
848 }
849 bool isLegal = true;
850 auto type = objectPair.value()[0].type();
851 if (type != nlohmann::json::value_t::object)
852 {
853 continue;
854 }
855
856 // verify legal json
857 for (const auto &arrayItem : objectPair.value())
858 {
859 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -0800860 {
James Feist8f2710a2018-05-09 17:18:55 -0700861 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -0800862 break;
863 }
James Feist8f2710a2018-05-09 17:18:55 -0700864 }
865 if (!isLegal)
866 {
867 std::cerr << "dbus format error" << objectPair.value()
868 << "\n";
869 break;
870 }
871
872 for (auto &arrayItem : objectPair.value())
873 {
James Feistbb43d022018-06-12 15:44:33 -0700874 // limit what interfaces accept set for safety
875 auto permission =
876 std::find(SETTABLE_INTERFACES.begin(),
877 SETTABLE_INTERFACES.end(),
878 objectPair.key()) !=
879 SETTABLE_INTERFACES.end()
880 ? sdbusplus::asio::PropertyPermission::readWrite
881 : sdbusplus::asio::PropertyPermission::readOnly;
James Feist97a63f12018-05-17 13:50:57 -0700882
James Feist8f2710a2018-05-09 17:18:55 -0700883 auto objectIface = objServer.add_interface(
884 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800885 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -0700886 "." + objectPair.key() + std::to_string(index));
887 populateInterfaceFromJson(systemConfiguration,
888 jsonPointerPath + "/" +
889 std::to_string(index),
890 objectIface.get(), arrayItem,
891 objServer, permission);
892 index++;
James Feist1b2e2242018-01-30 13:45:19 -0800893 }
894 }
895 }
896 }
897 }
898}
899
900// finds the template character (currently set to $) and replaces the value with
901// the field found in a dbus object i.e. $ADDRESS would get populated with the
902// ADDRESS field from a object on dbus
903void templateCharReplace(
904 nlohmann::json::iterator &keyPair,
James Feist8f2710a2018-05-09 17:18:55 -0700905 const boost::container::flat_map<std::string, BasicVariantType>
James Feist1b2e2242018-01-30 13:45:19 -0800906 &foundDevice,
907 size_t &foundDeviceIdx)
908{
James Feist11be6672018-04-06 14:05:32 -0700909 if (keyPair.value().type() == nlohmann::json::value_t::object)
910 {
911 for (auto nextLayer = keyPair.value().begin();
912 nextLayer != keyPair.value().end(); nextLayer++)
913 {
914 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
915 }
916 return;
917 }
918 else if (keyPair.value().type() != nlohmann::json::value_t::string)
James Feist1b2e2242018-01-30 13:45:19 -0800919 {
920 return;
921 }
922
923 std::string value = keyPair.value();
924 if (value.find(TEMPLATE_CHAR) != std::string::npos)
925 {
926 std::string templateValue = value;
927
928 templateValue.erase(0, 1); // remove template character
929
930 // special case index
931 if ("index" == templateValue)
932 {
933 keyPair.value() = foundDeviceIdx;
934 }
935 else
936 {
James Feist13b86d62018-05-29 11:24:35 -0700937 bool found = false;
James Feist1b2e2242018-01-30 13:45:19 -0800938 for (auto &foundDevicePair : foundDevice)
939 {
940 if (boost::iequals(foundDevicePair.first, templateValue))
941 {
James Feist13b86d62018-05-29 11:24:35 -0700942 mapbox::util::apply_visitor(
943 [&](auto &&val) { keyPair.value() = val; },
944 foundDevicePair.second);
945 found = true;
James Feist1b2e2242018-01-30 13:45:19 -0800946 break;
947 }
948 }
James Feist13b86d62018-05-29 11:24:35 -0700949 if (!found)
James Feist1b2e2242018-01-30 13:45:19 -0800950 {
951 std::cerr << "could not find symbol " << templateValue << "\n";
952 }
James Feist1b2e2242018-01-30 13:45:19 -0800953 }
954 }
955}
956
James Feist8f2710a2018-05-09 17:18:55 -0700957// reads json files out of the filesystem
958bool findJsonFiles(std::list<nlohmann::json> &configurations)
James Feist3cb5fec2018-01-23 14:41:51 -0800959{
960 // find configuration files
961 std::vector<fs::path> jsonPaths;
962 if (!find_files(fs::path(CONFIGURATION_DIR), R"(.*\.json)", jsonPaths, 0))
963 {
964 std::cerr << "Unable to find any configuration files in "
965 << CONFIGURATION_DIR << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -0800966 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800967 }
James Feist3cb5fec2018-01-23 14:41:51 -0800968 for (auto &jsonPath : jsonPaths)
969 {
970 std::ifstream jsonStream(jsonPath.c_str());
971 if (!jsonStream.good())
972 {
973 std::cerr << "unable to open " << jsonPath.string() << "\n";
974 continue;
975 }
976 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
977 if (data.is_discarded())
978 {
979 std::cerr << "syntax error in " << jsonPath.string() << "\n";
980 continue;
981 }
982 if (data.type() == nlohmann::json::value_t::array)
983 {
984 for (auto &d : data)
985 {
986 configurations.emplace_back(d);
987 }
988 }
989 else
990 {
991 configurations.emplace_back(data);
992 }
993 }
James Feist75fdeeb2018-02-20 14:26:16 -0800994}
James Feist3cb5fec2018-01-23 14:41:51 -0800995
James Feist8f2710a2018-05-09 17:18:55 -0700996struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -0800997{
James Feist75fdeeb2018-02-20 14:26:16 -0800998
James Feist8f2710a2018-05-09 17:18:55 -0700999 PerformScan(nlohmann::json &systemConfiguration,
1000 std::list<nlohmann::json> &configurations,
1001 std::function<void(void)> &&callback) :
1002 _systemConfiguration(systemConfiguration),
1003 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001004 {
James Feist8f2710a2018-05-09 17:18:55 -07001005 }
1006 void run()
1007 {
1008 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001009 {
James Feist1b2e2242018-01-30 13:45:19 -08001010 auto findProbe = it->find("probe");
James Feistd63d18a2018-07-19 15:23:45 -07001011 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001012
James Feist1b2e2242018-01-30 13:45:19 -08001013 nlohmann::json probeCommand;
1014 // check for poorly formatted fields, probe must be an array
1015 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001016 {
1017 std::cerr << "configuration file missing probe:\n " << *it
1018 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001019 it = _configurations.erase(it);
1020 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001021 }
James Feist1b2e2242018-01-30 13:45:19 -08001022 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001023 {
1024 probeCommand = nlohmann::json::array();
1025 probeCommand.push_back(*findProbe);
1026 }
1027 else
1028 {
1029 probeCommand = *findProbe;
1030 }
James Feist1b2e2242018-01-30 13:45:19 -08001031
1032 if (findName == it->end())
1033 {
1034 std::cerr << "configuration file missing name:\n " << *it
1035 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001036 it = _configurations.erase(it);
1037 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001038 }
James Feist8f2710a2018-05-09 17:18:55 -07001039 std::string name = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001040
James Feist8f2710a2018-05-09 17:18:55 -07001041 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(), name) !=
1042 PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001043 {
James Feist8f2710a2018-05-09 17:18:55 -07001044 it = _configurations.erase(it);
1045 continue;
1046 }
1047 nlohmann::json *record = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001048
James Feist8f2710a2018-05-09 17:18:55 -07001049 // store reference to this to children to makes sure we don't get
1050 // destroyed too early
1051 auto thisRef = shared_from_this();
1052 auto p = std::make_shared<PerformProbe>(
1053 probeCommand,
1054 [&, record, name,
1055 thisRef](std::vector<boost::container::flat_map<
1056 std::string, BasicVariantType>> &foundDevices) {
1057 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001058
James Feist8f2710a2018-05-09 17:18:55 -07001059 PASSED_PROBES.push_back(name);
1060 size_t foundDeviceIdx = 0;
1061
James Feistbe5425f2018-06-08 10:30:55 -07001062 // insert into configuration temporarly to be able to
1063 // reference ourselves
1064 _systemConfiguration[name] = *record;
1065
James Feist8f2710a2018-05-09 17:18:55 -07001066 for (auto &foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001067 {
James Feist8f2710a2018-05-09 17:18:55 -07001068 for (auto keyPair = record->begin();
1069 keyPair != record->end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001070 {
James Feist1b2e2242018-01-30 13:45:19 -08001071 templateCharReplace(keyPair, foundDevice,
1072 foundDeviceIdx);
James Feist8f2710a2018-05-09 17:18:55 -07001073 }
1074 auto findExpose = record->find("exposes");
1075 if (findExpose == record->end())
1076 {
1077 continue;
1078 }
1079 for (auto &expose : *findExpose)
1080 {
1081 for (auto keyPair = expose.begin();
1082 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001083 {
James Feist1b2e2242018-01-30 13:45:19 -08001084
James Feist8f2710a2018-05-09 17:18:55 -07001085 // fill in template characters with devices
1086 // found
1087 templateCharReplace(keyPair, foundDevice,
1088 foundDeviceIdx);
1089 // special case bind
1090 if (boost::starts_with(keyPair.key(), "bind_"))
1091 {
1092 if (keyPair.value().type() !=
1093 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001094 {
James Feist8f2710a2018-05-09 17:18:55 -07001095 std::cerr << "bind_ value must be of "
1096 "type string "
1097 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001098 continue;
1099 }
James Feist8f2710a2018-05-09 17:18:55 -07001100 bool foundBind = false;
1101 std::string bind = keyPair.key().substr(
1102 sizeof("bind_") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001103
James Feist8f2710a2018-05-09 17:18:55 -07001104 for (auto &configurationPair :
1105 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001106 {
James Feist1b2e2242018-01-30 13:45:19 -08001107
James Feist8f2710a2018-05-09 17:18:55 -07001108 auto configListFind =
1109 configurationPair.value().find(
1110 "exposes");
1111
1112 if (configListFind ==
1113 configurationPair.value()
1114 .end() ||
1115 configListFind->type() !=
1116 nlohmann::json::value_t::array)
1117 {
1118 continue;
1119 }
1120 for (auto &exposedObject :
1121 *configListFind)
1122 {
1123 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001124 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001125 if (boost::iequals(
1126 foundObjectName,
1127 keyPair.value()
1128 .get<std::string>()))
1129 {
1130 exposedObject["status"] =
1131 "okay";
1132 expose[bind] = exposedObject;
1133
1134 foundBind = true;
1135 break;
1136 }
1137 }
1138 if (foundBind)
1139 {
James Feist3cb5fec2018-01-23 14:41:51 -08001140 break;
1141 }
1142 }
James Feist8f2710a2018-05-09 17:18:55 -07001143 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001144 {
James Feist8f2710a2018-05-09 17:18:55 -07001145 std::cerr << "configuration file "
1146 "dependency error, "
1147 "could not find bind "
1148 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001149 }
1150 }
1151 }
1152 }
1153 }
James Feistbe5425f2018-06-08 10:30:55 -07001154 // overwrite ourselves with cleaned up version
James Feist8f2710a2018-05-09 17:18:55 -07001155 _systemConfiguration[name] = *record;
1156 });
1157 p->run();
1158 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001159 }
1160 }
James Feist75fdeeb2018-02-20 14:26:16 -08001161
James Feist8f2710a2018-05-09 17:18:55 -07001162 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001163 {
James Feist8f2710a2018-05-09 17:18:55 -07001164 if (_passed)
1165 {
1166 auto nextScan = std::make_shared<PerformScan>(
1167 _systemConfiguration, _configurations, std::move(_callback));
1168 nextScan->run();
1169 }
1170 else
1171 {
1172 _callback();
1173 }
1174 }
1175 nlohmann::json &_systemConfiguration;
1176 std::list<nlohmann::json> _configurations;
1177 std::function<void(void)> _callback;
1178 std::vector<std::shared_ptr<PerformProbe>> _probes;
1179 bool _passed = false;
1180};
James Feistc95cb142018-02-26 10:41:42 -08001181
James Feist8f2710a2018-05-09 17:18:55 -07001182// main properties changed entry
1183void propertiesChangedCallback(
1184 boost::asio::io_service &io,
1185 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1186 nlohmann::json &systemConfiguration,
1187 sdbusplus::asio::object_server &objServer)
1188{
1189 static boost::asio::deadline_timer timer(io);
1190 timer.expires_from_now(boost::posix_time::seconds(1));
1191
1192 // setup an async wait as we normally get flooded with new requests
1193 timer.async_wait([&](const boost::system::error_code &ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001194 if (ec == boost::asio::error::operation_aborted)
1195 {
1196 // we were cancelled
1197 return;
1198 }
1199 else if (ec)
1200 {
1201 std::cerr << "async wait error " << ec << "\n";
1202 return;
1203 }
1204
1205 nlohmann::json oldConfiguration = systemConfiguration;
1206 DBUS_PROBE_OBJECTS.clear();
1207
1208 std::list<nlohmann::json> configurations;
1209 if (!findJsonFiles(configurations))
1210 {
1211 std::cerr << "cannot find json files\n";
1212 return;
1213 }
1214
1215 auto perfScan = std::make_shared<PerformScan>(
1216 systemConfiguration, configurations, [&, oldConfiguration]() {
1217 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001218 for (auto it = newConfiguration.begin();
1219 it != newConfiguration.end();)
1220 {
1221 auto findKey = oldConfiguration.find(it.key());
1222 if (findKey != oldConfiguration.end())
1223 {
1224 it = newConfiguration.erase(it);
1225 }
1226 else
1227 {
1228 it++;
1229 }
1230 }
James Feist8f2710a2018-05-09 17:18:55 -07001231 registerCallbacks(io, dbusMatches, systemConfiguration,
1232 objServer);
1233 io.post([&, newConfiguration]() {
1234 // todo: for now, only add new configurations,
1235 // unload to come later unloadOverlays();
1236 loadOverlays(newConfiguration);
James Feistbb43d022018-06-12 15:44:33 -07001237 io.post([&]() {
1238 if (!writeJsonFiles(systemConfiguration))
1239 {
1240 std::cerr << "Error writing json files\n";
1241 }
1242 });
James Feist8f2710a2018-05-09 17:18:55 -07001243 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001244 postToDbus(newConfiguration, systemConfiguration,
1245 objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001246 });
1247 });
1248 });
1249 perfScan->run();
1250 });
James Feist75fdeeb2018-02-20 14:26:16 -08001251}
1252
James Feist8f2710a2018-05-09 17:18:55 -07001253void registerCallbacks(boost::asio::io_service &io,
1254 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1255 nlohmann::json &systemConfiguration,
1256 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001257{
1258 static boost::container::flat_set<std::string> watchedObjects;
1259
1260 for (const auto &objectMap : DBUS_PROBE_OBJECTS)
1261 {
1262 auto findObject = watchedObjects.find(objectMap.first);
1263 if (findObject != watchedObjects.end())
1264 {
1265 continue;
1266 }
James Feist8f2710a2018-05-09 17:18:55 -07001267 std::function<void(sdbusplus::message::message & message)>
1268 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001269
James Feist8f2710a2018-05-09 17:18:55 -07001270 [&](sdbusplus::message::message &) {
1271 propertiesChangedCallback(io, dbusMatches,
1272 systemConfiguration, objServer);
1273 };
1274
1275 sdbusplus::bus::match::match match(
1276 static_cast<sdbusplus::bus::bus &>(*SYSTEM_BUS),
1277 "type='signal',member='PropertiesChanged',arg0='" +
1278 objectMap.first + "'",
1279 eventHandler);
1280 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001281 }
1282}
1283
1284int main(int argc, char **argv)
1285{
1286 // setup connection to dbus
1287 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001288 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001289 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001290
James Feist8f2710a2018-05-09 17:18:55 -07001291 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001292
James Feist8f2710a2018-05-09 17:18:55 -07001293 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1294 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1295 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001296
James Feist8f2710a2018-05-09 17:18:55 -07001297 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1298 objServer.add_interface("/xyz/openbmc_project/inventory",
1299 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001300
1301 // to keep reference to the match / filter objects so they don't get
1302 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001303 std::vector<sdbusplus::bus::match::match> dbusMatches;
1304
1305 nlohmann::json systemConfiguration = nlohmann::json::object();
1306
1307 inventoryIface->register_method(
1308 "Notify", [](const boost::container::flat_map<
1309 std::string,
1310 boost::container::flat_map<std::string, BasicVariantType>>
1311 &object) { return; });
1312 inventoryIface->initialize();
1313
1314 io.post([&]() {
1315 unloadAllOverlays();
1316 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1317 objServer);
1318 });
James Feist4131aea2018-03-09 09:47:30 -08001319
James Feistfd1264a2018-05-03 12:10:00 -07001320 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001321 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1322 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001323 });
James Feist8f2710a2018-05-09 17:18:55 -07001324 entityIface->initialize();
1325
James Feist1b2e2242018-01-30 13:45:19 -08001326 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001327
1328 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001329}