blob: 19a370b7b172b74404ce027c3f8b97d52bdcc37f [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 <dbus/properties.hpp>
20#include <nlohmann/json.hpp>
21#include <fstream>
James Feist75fdeeb2018-02-20 14:26:16 -080022#include <future>
James Feist3cb5fec2018-01-23 14:41:51 -080023#include <regex>
24#include <boost/algorithm/string/predicate.hpp>
25#include <boost/algorithm/string/replace.hpp>
26#include <boost/variant/apply_visitor.hpp>
27#include <boost/lexical_cast.hpp>
28#include <boost/container/flat_map.hpp>
29#include <boost/container/flat_set.hpp>
James Feist1b2e2242018-01-30 13:45:19 -080030#include <dbus/connection.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080031#include <VariantVisitors.hpp>
James Feist7b7e4e82018-01-24 14:56:00 -080032#include <experimental/filesystem>
James Feist3cb5fec2018-01-23 14:41:51 -080033
34constexpr const char *OUTPUT_DIR = "/var/configuration/";
35constexpr const char *CONFIGURATION_DIR = "/usr/share/configurations";
36constexpr const char *TEMPLATE_CHAR = "$";
James Feistc95cb142018-02-26 10:41:42 -080037constexpr const size_t PROPERTIES_CHANGED_UNTIL_FLUSH_COUNT = 20;
James Feist3cb5fec2018-01-23 14:41:51 -080038constexpr const size_t MAX_MAPPER_DEPTH = 99;
James Feistc95cb142018-02-26 10:41:42 -080039constexpr const size_t SLEEP_AFTER_PROPERTIES_CHANGE_SECONDS = 3;
James Feist3cb5fec2018-01-23 14:41:51 -080040
41namespace fs = std::experimental::filesystem;
42struct cmp_str
43{
44 bool operator()(const char *a, const char *b) const
45 {
46 return std::strcmp(a, b) < 0;
47 }
48};
49
50// underscore T for collison with dbus c api
51enum class probe_type_codes
52{
53 FALSE_T,
54 TRUE_T,
55 AND,
56 OR,
57 FOUND
58};
59const static boost::container::flat_map<const char *, probe_type_codes, cmp_str>
60 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
61 {"TRUE", probe_type_codes::TRUE_T},
62 {"AND", probe_type_codes::AND},
63 {"OR", probe_type_codes::OR},
64 {"FOUND", probe_type_codes::FOUND}}};
65
66using GetSubTreeType = std::vector<
67 std::pair<std::string,
68 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
69
70using ManagedObjectType = boost::container::flat_map<
71 dbus::object_path,
72 boost::container::flat_map<
73 std::string,
74 boost::container::flat_map<std::string, dbus::dbus_variant>>>;
75
76boost::container::flat_map<
77 std::string,
78 std::vector<boost::container::flat_map<std::string, dbus::dbus_variant>>>
79 DBUS_PROBE_OBJECTS;
80std::vector<std::string> PASSED_PROBES;
81
82// todo: pass this through nicer
83std::shared_ptr<dbus::connection> SYSTEM_BUS;
84
James Feist1b2e2242018-01-30 13:45:19 -080085std::regex ILLEGAL_DBUS_REGEX("[^A-Za-z0-9_]");
86
James Feist75fdeeb2018-02-20 14:26:16 -080087void registerCallbacks(
88 std::vector<std::pair<std::unique_ptr<dbus::match>,
89 std::shared_ptr<dbus::filter>>> &dbusMatches,
90 std::atomic_bool &threadRunning, nlohmann::json &systemConfiguration,
91 dbus::DbusObjectServer &objServer);
92
James Feist3cb5fec2018-01-23 14:41:51 -080093// calls the mapper to find all exposed objects of an interface type
94// and creates a vector<flat_map> that contains all the key value pairs
95// getManagedObjects
96bool findDbusObjects(
97 std::shared_ptr<dbus::connection> connection,
98 std::vector<boost::container::flat_map<std::string, dbus::dbus_variant>>
99 &interfaceDevices,
100 std::string interface)
101{
102 // find all connections in the mapper that expose a specific type
103 static const dbus::endpoint mapper("xyz.openbmc_project.ObjectMapper",
104 "/xyz/openbmc_project/object_mapper",
105 "xyz.openbmc_project.ObjectMapper",
106 "GetSubTree");
107 dbus::message getMap = dbus::message::new_call(mapper);
108 std::vector<std::string> objects = {interface};
109 if (!getMap.pack("", MAX_MAPPER_DEPTH, objects))
110 {
111 std::cerr << "Pack Failed GetSensorSubtree\n";
112 return false;
113 }
114 dbus::message getMapResp = connection->send(getMap);
115 GetSubTreeType interfaceSubtree;
116 if (!getMapResp.unpack(interfaceSubtree))
117 {
118 std::cerr << "Error communicating to mapper\n";
119 return false;
120 }
121 boost::container::flat_set<std::string> connections;
122 for (auto &object : interfaceSubtree)
123 {
124 for (auto &connPair : object.second)
125 {
126 connections.insert(connPair.first);
127 }
128 }
129 // iterate through the connections, adding creating individual device
130 // dictionaries
131 for (auto &conn : connections)
132 {
133 auto managedObj =
134 dbus::endpoint(conn, "/", "org.freedesktop.DBus.ObjectManager",
135 "GetManagedObjects");
136 dbus::message getManagedObj = dbus::message::new_call(managedObj);
137 dbus::message getManagedObjResp = connection->send(getManagedObj);
138 ManagedObjectType managedInterface;
139 if (!getManagedObjResp.unpack(managedInterface))
140 {
141 std::cerr << "error getting managed object for device " << conn
142 << "\n";
143 continue;
144 }
145 for (auto &interfaceManagedObj : managedInterface)
146 {
147 auto ifaceObjFind = interfaceManagedObj.second.find(interface);
148 if (ifaceObjFind != interfaceManagedObj.second.end())
149 {
150 interfaceDevices.emplace_back(ifaceObjFind->second);
151 }
152 }
153 }
154 return true;
155}
156
157// probes interface dictionary for a key with a value that matches a regex
158bool probeDbus(
159 const std::string &interface,
160 const std::map<std::string, nlohmann::json> &matches,
161 std::vector<boost::container::flat_map<std::string, dbus::dbus_variant>>
162 &devices,
163 bool &foundProbe)
164{
165 auto &dbusObject = DBUS_PROBE_OBJECTS[interface];
166 if (dbusObject.empty())
167 {
168 if (!findDbusObjects(SYSTEM_BUS, dbusObject, interface))
169 {
170 std::cerr << "Found no dbus objects with interface "
171 << interface << "\n";
172 foundProbe = false;
173 return false;
174 }
175 }
176 foundProbe = true;
177
178 bool foundMatch = false;
179 for (auto &device : dbusObject)
180 {
181 bool deviceMatches = true;
182 for (auto &match : matches)
183 {
184 auto deviceValue = device.find(match.first);
185 if (deviceValue != device.end())
186 {
187 switch (match.second.type())
188 {
189 case nlohmann::json::value_t::string:
190 {
191 std::regex search(match.second.get<std::string>());
192 std::smatch match;
193
194 // convert value to string respresentation
195 std::string probeValue = boost::apply_visitor(
196 [](const auto &x) {
197 return boost::lexical_cast<std::string>(x);
198 },
199 deviceValue->second);
200 if (!std::regex_search(probeValue, match, search))
201 {
202 deviceMatches = false;
203 break;
204 }
205 break;
206 }
207 case nlohmann::json::value_t::boolean:
208 case nlohmann::json::value_t::number_unsigned:
209 {
210 unsigned int probeValue = boost::apply_visitor(
211 VariantToUnsignedIntVisitor(), deviceValue->second);
212
213 if (probeValue != match.second.get<unsigned int>())
214 {
215 deviceMatches = false;
216 }
217 break;
218 }
219 case nlohmann::json::value_t::number_integer:
220 {
221 int probeValue = boost::apply_visitor(VariantToIntVisitor(),
222 deviceValue->second);
223
224 if (probeValue != match.second.get<int>())
225 {
226 deviceMatches = false;
227 }
228 break;
229 }
230 case nlohmann::json::value_t::number_float:
231 {
232 float probeValue = boost::apply_visitor(
233 VariantToFloatVisitor(), deviceValue->second);
234
235 if (probeValue != match.second.get<float>())
236 {
237 deviceMatches = false;
238 }
239 break;
240 }
241 }
242 }
243 else
244 {
245 deviceMatches = false;
246 break;
247 }
248 }
249 if (deviceMatches)
250 {
251 devices.emplace_back(
252 boost::container::flat_map<std::string, dbus::dbus_variant>(
253 device));
254 foundMatch = true;
255 deviceMatches = false; // for next iteration
256 }
257 }
258 return foundMatch;
259}
260
261// default probe entry point, iterates a list looking for specific types to
262// call specific probe functions
263bool probe(
264 const std::vector<std::string> probeCommand,
265 std::vector<boost::container::flat_map<std::string, dbus::dbus_variant>>
266 &foundDevs)
267{
268 const static std::regex command(R"(\((.*)\))");
269 std::smatch match;
270 bool ret = false;
271 bool cur = true;
272 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
273
274 for (auto &probe : probeCommand)
275 {
276 bool foundProbe = false;
277 boost::container::flat_map<const char *, probe_type_codes,
278 cmp_str>::const_iterator probeType;
279
280 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
281 probeType++)
282 {
283 if (probe.find(probeType->first) != std::string::npos)
284 {
285 foundProbe = true;
286 break;
287 }
288 }
289 if (foundProbe)
290 {
291 switch (probeType->second)
292 {
293 case probe_type_codes::FALSE_T:
294 {
295 return false; // todo, actually evaluate?
296 break;
297 }
298 case probe_type_codes::TRUE_T:
299 {
300 return true; // todo, actually evaluate?
301 break;
302 }
303 /*case probe_type_codes::AND:
304 break;
305 case probe_type_codes::OR:
306 break;
307 // these are no-ops until the last command switch
308 */
309 case probe_type_codes::FOUND:
310 {
311 if (!std::regex_search(probe, match, command))
312 {
313 std::cerr << "found probe sytax error " << probe << "\n";
314 return false;
315 }
316 std::string commandStr = *(match.begin() + 1);
James Feist3f8a2782018-02-12 09:24:42 -0800317 boost::replace_all(commandStr, "'", "");
James Feist3cb5fec2018-01-23 14:41:51 -0800318 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
319 commandStr) != PASSED_PROBES.end());
320 break;
321 }
322 }
323 }
324 // look on dbus for object
325 else
326 {
327 if (!std::regex_search(probe, match, command))
328 {
329 std::cerr << "dbus probe sytax error " << probe << "\n";
330 return false;
331 }
332 std::string commandStr = *(match.begin() + 1);
333 // convert single ticks and single slashes into legal json
James Feist3f8a2782018-02-12 09:24:42 -0800334 boost::replace_all(commandStr, "'", R"(")");
335 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800336 auto json = nlohmann::json::parse(commandStr, nullptr, false);
337 if (json.is_discarded())
338 {
339 std::cerr << "dbus command sytax error " << commandStr << "\n";
340 return false;
341 }
342 // we can match any (string, variant) property. (string, string)
343 // does a regex
344 std::map<std::string, nlohmann::json> dbusProbeMap =
345 json.get<std::map<std::string, nlohmann::json>>();
346 auto findStart = probe.find("(");
347 if (findStart == std::string::npos)
348 {
349 return false;
350 }
351 std::string probeInterface = probe.substr(0, findStart);
352 cur =
353 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
354 }
355
356 // some functions like AND and OR only take affect after the
357 // fact
358 switch (lastCommand)
359 {
360 case probe_type_codes::AND:
361 ret = cur && ret;
362 break;
363 case probe_type_codes::OR:
364 ret = cur || ret;
365 break;
366 default:
367 ret = cur;
368 break;
369 }
370 lastCommand = probeType != PROBE_TYPES.end()
371 ? probeType->second
372 : probe_type_codes::FALSE_T;
373
374 if (!foundProbe)
375 {
376 std::cerr << "Illegal probe type " << probe << "\n";
377 return false;
378 }
379 }
380
381 // probe passed, but empty device
382 // todo: should this be done in main?
383 if (ret && foundDevs.size() == 0)
384 {
385 foundDevs.emplace_back(
386 boost::container::flat_map<std::string, dbus::dbus_variant>());
387 }
388 return ret;
389}
390
James Feist1b2e2242018-01-30 13:45:19 -0800391// this function is temporary, no need to have once dbus is solified.
392void writeJsonFiles(nlohmann::json &systemConfiguration)
393{
394 std::experimental::filesystem::create_directory(OUTPUT_DIR);
395 std::ofstream output(std::string(OUTPUT_DIR) + "system.json");
396 output << systemConfiguration.dump(4);
397 output.close();
398
399 auto flat = nlohmann::json::array();
400 for (auto &pair : nlohmann::json::iterator_wrapper(systemConfiguration))
401 {
402 auto value = pair.value();
403 auto exposes = value.find("exposes");
404 if (exposes != value.end())
405 {
406 for (auto &item : *exposes)
407 {
408 flat.push_back(item);
409 }
410 }
411 }
412 output = std::ofstream(std::string(OUTPUT_DIR) + "flattened.json");
413 output << flat.dump(4);
414 output.close();
415}
416// adds simple json types to interface's properties
417void populateInterfaceFromJson(dbus::DbusInterface *iface, nlohmann::json dict,
418 dbus::DbusObjectServer &objServer)
419{
420 std::vector<std::pair<std::string, dbus::dbus_variant>> properties;
421 static size_t flushCount = 0;
422
423 for (auto &dictPair : nlohmann::json::iterator_wrapper(dict))
424 {
425 switch (dictPair.value().type())
426 {
427 case (nlohmann::json::value_t::boolean):
428 {
429 properties.emplace_back(std::string(dictPair.key()),
430 dictPair.value().get<bool>());
431 break;
432 }
433 case (nlohmann::json::value_t::number_integer):
434 {
435 properties.emplace_back(std::string(dictPair.key()),
436 dictPair.value().get<int64_t>());
437 break;
438 }
439 case (nlohmann::json::value_t::number_unsigned):
440 {
441 properties.emplace_back(std::string(dictPair.key()),
442 dictPair.value().get<uint64_t>());
443 break;
444 }
445 case (nlohmann::json::value_t::number_float):
446 {
447 properties.emplace_back(std::string(dictPair.key()),
448 dictPair.value().get<float>());
449 break;
450 }
451 case (nlohmann::json::value_t::string):
452 {
453 properties.emplace_back(std::string(dictPair.key()),
454 dictPair.value().get<std::string>());
455 break;
456 }
457 }
458 }
459 if (!properties.empty())
460 {
461 iface->set_properties(properties);
462
463 // flush the queue after adding an amount of properties so we don't hang
James Feistc95cb142018-02-26 10:41:42 -0800464 if (flushCount++ > PROPERTIES_CHANGED_UNTIL_FLUSH_COUNT)
James Feist1b2e2242018-01-30 13:45:19 -0800465 {
466 objServer.flush();
467 flushCount = 0;
468 }
469 }
470}
471
472void postToDbus(const nlohmann::json &systemConfiguration,
James Feist75fdeeb2018-02-20 14:26:16 -0800473 dbus::DbusObjectServer &objServer)
474
James Feist1b2e2242018-01-30 13:45:19 -0800475{
476 for (auto &boardPair :
477 nlohmann::json::iterator_wrapper(systemConfiguration))
478 {
479 std::string boardKey = boardPair.key();
480 auto boardValues = boardPair.value();
481 auto findBoardType = boardValues.find("type");
482 std::string boardType;
483 if (findBoardType != boardValues.end() &&
484 findBoardType->type() == nlohmann::json::value_t::string)
485 {
486 boardType = findBoardType->get<std::string>();
487 std::regex_replace(boardType.begin(), boardType.begin(),
488 boardType.end(), ILLEGAL_DBUS_REGEX, "_");
489 }
490 else
491 {
492 std::cerr << "Unable to find type for " << boardKey
493 << " reverting to Chassis.\n";
494 boardType = "Chassis";
495 }
496
497 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
498 ILLEGAL_DBUS_REGEX, "_");
499 std::string boardName =
500 "/xyz/openbmc_project/Inventory/Item/" + boardType + "/" + boardKey;
501 auto boardObject = objServer.add_object(boardName);
502
503 auto boardIface = boardObject->add_interface(
504 "xyz.openbmc_project.Configuration." + boardType);
505 populateInterfaceFromJson(boardIface.get(), boardValues, objServer);
506 auto exposes = boardValues.find("exposes");
507 if (exposes == boardValues.end())
508 {
509 continue;
510 }
511 for (auto &item : *exposes)
512 {
513 auto findName = item.find("name");
514 if (findName == item.end())
515 {
516 std::cerr << "cannot find name in field " << item << "\n";
517 continue;
518 }
519 auto findStatus = item.find("status");
520 // if status is not found it is assumed to be status = 'okay'
521 if (findStatus != item.end())
522 {
523 if (*findStatus == "disabled")
524 {
525 continue;
526 }
527 }
528 auto findType = item.find("type");
529 std::string itemType;
530 if (findType != item.end())
531 {
532 itemType = findType->get<std::string>();
533 std::regex_replace(itemType.begin(), itemType.begin(),
534 itemType.end(), ILLEGAL_DBUS_REGEX, "_");
535 }
536 else
537 {
538 itemType = "unknown";
539 }
540 std::string itemName = findName->get<std::string>();
541 std::regex_replace(itemName.begin(), itemName.begin(),
542 itemName.end(), ILLEGAL_DBUS_REGEX, "_");
543 auto itemObject = objServer.add_object(boardName + "/" + itemName);
544 auto itemIface = itemObject->add_interface(
545 "xyz.openbmc_project.Configuration." + itemType);
546
547 populateInterfaceFromJson(itemIface.get(), item, objServer);
548
549 for (auto &objectPair : nlohmann::json::iterator_wrapper(item))
550 {
551 if (objectPair.value().type() ==
552 nlohmann::json::value_t::object)
553 {
554 auto objectIface = itemObject->add_interface(
555 "xyz.openbmc_project.Configuration." + itemType + "." +
556 objectPair.key());
557 populateInterfaceFromJson(objectIface.get(),
558 objectPair.value(), objServer);
559 }
560 else if (objectPair.value().type() ==
561 nlohmann::json::value_t::array)
562 {
563 size_t index = 0;
564 for (auto &arrayItem : objectPair.value())
565 {
566 if (arrayItem.type() != nlohmann::json::value_t::object)
567 {
568 std::cerr << "dbus format error" << arrayItem
569 << "\n";
570 break;
571 }
572 auto objectIface = itemObject->add_interface(
573 "xyz.openbmc_project.Configuration." + itemType +
574 "." + objectPair.key() + "." +
575 std::to_string(index));
576 index++;
577 populateInterfaceFromJson(objectIface.get(), arrayItem,
578 objServer);
579 }
580 }
581 }
582 }
583 }
584}
585
586// finds the template character (currently set to $) and replaces the value with
587// the field found in a dbus object i.e. $ADDRESS would get populated with the
588// ADDRESS field from a object on dbus
589void templateCharReplace(
590 nlohmann::json::iterator &keyPair,
591 const boost::container::flat_map<std::string, dbus::dbus_variant>
592 &foundDevice,
593 size_t &foundDeviceIdx)
594{
595 if (keyPair.value().type() != nlohmann::json::value_t::string)
596 {
597 return;
598 }
599
600 std::string value = keyPair.value();
601 if (value.find(TEMPLATE_CHAR) != std::string::npos)
602 {
603 std::string templateValue = value;
604
605 templateValue.erase(0, 1); // remove template character
606
607 // special case index
608 if ("index" == templateValue)
609 {
610 keyPair.value() = foundDeviceIdx;
611 }
612 else
613 {
614 std::string subsitute;
615 for (auto &foundDevicePair : foundDevice)
616 {
617 if (boost::iequals(foundDevicePair.first, templateValue))
618 {
619 // convert value to string
620 // respresentation
621 subsitute = boost::apply_visitor(
622 [](const auto &x) {
623 return boost::lexical_cast<std::string>(x);
624 },
625 foundDevicePair.second);
626 break;
627 }
628 }
629 if (!subsitute.size())
630 {
631 std::cerr << "could not find symbol " << templateValue << "\n";
632 }
633 else
634 {
635 keyPair.value() = subsitute;
636 }
637 }
638 }
639}
640
James Feist75fdeeb2018-02-20 14:26:16 -0800641bool findJsonFiles(std::vector<nlohmann::json> &configurations)
James Feist3cb5fec2018-01-23 14:41:51 -0800642{
643 // find configuration files
644 std::vector<fs::path> jsonPaths;
645 if (!find_files(fs::path(CONFIGURATION_DIR), R"(.*\.json)", jsonPaths, 0))
646 {
647 std::cerr << "Unable to find any configuration files in "
648 << CONFIGURATION_DIR << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -0800649 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800650 }
James Feist3cb5fec2018-01-23 14:41:51 -0800651 for (auto &jsonPath : jsonPaths)
652 {
653 std::ifstream jsonStream(jsonPath.c_str());
654 if (!jsonStream.good())
655 {
656 std::cerr << "unable to open " << jsonPath.string() << "\n";
657 continue;
658 }
659 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
660 if (data.is_discarded())
661 {
662 std::cerr << "syntax error in " << jsonPath.string() << "\n";
663 continue;
664 }
665 if (data.type() == nlohmann::json::value_t::array)
666 {
667 for (auto &d : data)
668 {
669 configurations.emplace_back(d);
670 }
671 }
672 else
673 {
674 configurations.emplace_back(data);
675 }
676 }
James Feist75fdeeb2018-02-20 14:26:16 -0800677}
James Feist3cb5fec2018-01-23 14:41:51 -0800678
James Feist75fdeeb2018-02-20 14:26:16 -0800679bool rescan(nlohmann::json &systemConfiguration)
680{
681 std::vector<nlohmann::json> configurations;
682 if (!findJsonFiles(configurations))
683 {
684 false;
685 }
686 // preprocess already passed configurations and missing fields
687 if (systemConfiguration.size())
688 {
689 for (auto it = configurations.begin(); it != configurations.end();)
690 {
691 auto findName = it->find("name");
692 if (findName == it->end())
693 {
694 std::cerr << "configuration missing name field " << *it << "\n";
695 it = configurations.erase(it);
696 continue;
697 }
698 else if (findName->type() != nlohmann::json::value_t::string)
699 {
700 std::cerr << "name field must be a string " << *findName
701 << "\n";
702 it = configurations.erase(it);
703 continue;
704 }
705 auto findAlreadyFound =
706 systemConfiguration.find(findName->get<std::string>());
707 if (findAlreadyFound != systemConfiguration.end())
708 {
709 it = configurations.erase(it);
710 continue;
711 }
712 // TODO: add in tags to determine if configuration should be
713 // refreshed on AC / DC / Always.
714 it++;
715 }
716 }
717
718 // probe until no probes pass
James Feist3cb5fec2018-01-23 14:41:51 -0800719 bool probePassed = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800720 while (probePassed)
721 {
722 probePassed = false;
723 for (auto it = configurations.begin(); it != configurations.end();)
724 {
725 bool eraseConfig = false;
James Feist1b2e2242018-01-30 13:45:19 -0800726 auto findProbe = it->find("probe");
727 auto findName = it->find("name");
James Feist3cb5fec2018-01-23 14:41:51 -0800728
James Feist1b2e2242018-01-30 13:45:19 -0800729 nlohmann::json probeCommand;
730 // check for poorly formatted fields, probe must be an array
731 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -0800732 {
733 std::cerr << "configuration file missing probe:\n " << *it
734 << "\n";
735 eraseConfig = true;
736 }
James Feist1b2e2242018-01-30 13:45:19 -0800737 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -0800738 {
739 probeCommand = nlohmann::json::array();
740 probeCommand.push_back(*findProbe);
741 }
742 else
743 {
744 probeCommand = *findProbe;
745 }
James Feist1b2e2242018-01-30 13:45:19 -0800746
747 if (findName == it->end())
748 {
749 std::cerr << "configuration file missing name:\n " << *it
750 << "\n";
751 eraseConfig = true;
752 }
753
James Feist3cb5fec2018-01-23 14:41:51 -0800754 std::vector<
755 boost::container::flat_map<std::string, dbus::dbus_variant>>
756 foundDevices;
James Feist1b2e2242018-01-30 13:45:19 -0800757 if (!eraseConfig && probe(probeCommand, foundDevices))
James Feist3cb5fec2018-01-23 14:41:51 -0800758 {
759 eraseConfig = true;
760 probePassed = true;
James Feist7b7e4e82018-01-24 14:56:00 -0800761 std::string name = *findName;
762 PASSED_PROBES.push_back(name);
James Feist3cb5fec2018-01-23 14:41:51 -0800763
764 size_t foundDeviceIdx = 0;
765
766 for (auto &foundDevice : foundDevices)
767 {
James Feist1b2e2242018-01-30 13:45:19 -0800768 for (auto keyPair = it->begin(); keyPair != it->end();
769 keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -0800770 {
James Feist1b2e2242018-01-30 13:45:19 -0800771 templateCharReplace(keyPair, foundDevice,
772 foundDeviceIdx);
773 }
774 auto findExpose = it->find("exposes");
775 if (findExpose == it->end())
776 {
James Feist3cb5fec2018-01-23 14:41:51 -0800777 continue;
778 }
779 for (auto &expose : *findExpose)
780 {
781 for (auto keyPair = expose.begin();
782 keyPair != expose.end(); keyPair++)
783 {
James Feist1b2e2242018-01-30 13:45:19 -0800784
James Feist3cb5fec2018-01-23 14:41:51 -0800785 // fill in template characters with devices
786 // found
James Feist1b2e2242018-01-30 13:45:19 -0800787 templateCharReplace(keyPair, foundDevice,
788 foundDeviceIdx);
789 // special case bind
790 if (boost::starts_with(keyPair.key(), "bind_"))
James Feist3cb5fec2018-01-23 14:41:51 -0800791 {
James Feist1b2e2242018-01-30 13:45:19 -0800792 if (keyPair.value().type() !=
793 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -0800794 {
James Feist1b2e2242018-01-30 13:45:19 -0800795 std::cerr
796 << "bind_ value must be of type string "
797 << keyPair.key() << "\n";
798 continue;
James Feist3cb5fec2018-01-23 14:41:51 -0800799 }
James Feist1b2e2242018-01-30 13:45:19 -0800800 bool foundBind = false;
801 std::string bind =
802 keyPair.key().substr(sizeof("bind_") - 1);
803 for (auto &configurationPair :
804 nlohmann::json::iterator_wrapper(
805 systemConfiguration))
James Feist3cb5fec2018-01-23 14:41:51 -0800806 {
James Feist1b2e2242018-01-30 13:45:19 -0800807
808 auto configListFind =
809 configurationPair.value().find(
810 "exposes");
811
812 if (configListFind ==
813 configurationPair.value().end() ||
814 configListFind->type() !=
815 nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -0800816 {
James Feist1b2e2242018-01-30 13:45:19 -0800817 continue;
818 }
819 for (auto &exposedObject : *configListFind)
820 {
821 std::string foundObjectName =
822 (exposedObject)["name"];
823 if (boost::iequals(
824 foundObjectName,
825 keyPair.value()
826 .get<std::string>()))
James Feist3cb5fec2018-01-23 14:41:51 -0800827 {
James Feistc95cb142018-02-26 10:41:42 -0800828 exposedObject["status"] = "okay";
James Feist1b2e2242018-01-30 13:45:19 -0800829 expose[bind] = exposedObject;
James Feist1b2e2242018-01-30 13:45:19 -0800830
831 foundBind = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800832 break;
833 }
834 }
James Feist1b2e2242018-01-30 13:45:19 -0800835 if (foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -0800836 {
James Feist1b2e2242018-01-30 13:45:19 -0800837 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800838 }
839 }
James Feist1b2e2242018-01-30 13:45:19 -0800840 if (!foundBind)
841 {
842 std::cerr << "configuration file "
843 "dependency error, "
844 "could not find bind "
845 << keyPair.value() << "\n";
846 }
James Feist3cb5fec2018-01-23 14:41:51 -0800847 }
848 }
849 }
James Feist3cb5fec2018-01-23 14:41:51 -0800850 }
James Feist1b2e2242018-01-30 13:45:19 -0800851 systemConfiguration[name] = (*it);
852 foundDeviceIdx++;
James Feist3cb5fec2018-01-23 14:41:51 -0800853 }
854
855 if (eraseConfig)
856 {
857 it = configurations.erase(it);
858 }
859 else
860 {
861 it++;
862 }
863 }
864 }
James Feist75fdeeb2018-02-20 14:26:16 -0800865}
866
867void propertiesChangedCallback(
868 std::vector<std::pair<std::unique_ptr<dbus::match>,
869 std::shared_ptr<dbus::filter>>> &dbusMatches,
870 std::atomic_bool &threadRunning, nlohmann::json &systemConfiguration,
871 dbus::DbusObjectServer &objServer, std::shared_ptr<dbus::filter> dbusFilter)
872{
873 static std::future<void> future;
874 bool notRunning = false;
875 if (threadRunning.compare_exchange_strong(notRunning, true))
876 {
877 future = std::async(std::launch::async, [&] {
James Feistc95cb142018-02-26 10:41:42 -0800878 std::this_thread::sleep_for(
879 std::chrono::seconds(SLEEP_AFTER_PROPERTIES_CHANGE_SECONDS));
James Feist75fdeeb2018-02-20 14:26:16 -0800880 auto oldConfiguration = systemConfiguration;
881 DBUS_PROBE_OBJECTS.clear();
882 rescan(systemConfiguration);
883 auto newConfiguration = systemConfiguration;
884 for (auto it = newConfiguration.begin();
885 it != newConfiguration.end();)
886 {
887 auto findKey = oldConfiguration.find(it.key());
888 if (findKey != oldConfiguration.end())
889 {
890 it = newConfiguration.erase(it);
891 }
892 else
893 {
894 it++;
895 }
896 }
James Feistc95cb142018-02-26 10:41:42 -0800897 // todo: for now, only add new configurations, unload to come later
898 // unloadOverlays();
899 loadOverlays(newConfiguration);
James Feist75fdeeb2018-02-20 14:26:16 -0800900 // this line to be removed in future
901 writeJsonFiles(systemConfiguration);
James Feist1ba95ac2018-03-02 14:17:35 -0800902 // only post new items to bus for now
903 postToDbus(newConfiguration, objServer);
James Feistc95cb142018-02-26 10:41:42 -0800904
James Feist75fdeeb2018-02-20 14:26:16 -0800905 registerCallbacks(dbusMatches, threadRunning, systemConfiguration,
906 objServer);
907 threadRunning = false;
908 });
909 }
910 if (dbusFilter != nullptr)
911 {
912 dbusFilter->async_dispatch([&, dbusFilter](boost::system::error_code ec,
913 dbus::message) {
914 if (ec)
915 {
916 std::cerr << "properties changed callback error " << ec << "\n";
917 }
918 propertiesChangedCallback(dbusMatches, threadRunning,
919 systemConfiguration, objServer,
920 dbusFilter);
921 });
922 }
923}
924
925void registerCallbacks(
926 std::vector<std::pair<std::unique_ptr<dbus::match>,
927 std::shared_ptr<dbus::filter>>> &dbusMatches,
928 std::atomic_bool &threadRunning, nlohmann::json &systemConfiguration,
929 dbus::DbusObjectServer &objServer)
930{
931 static boost::container::flat_set<std::string> watchedObjects;
932
933 for (const auto &objectMap : DBUS_PROBE_OBJECTS)
934 {
935 auto findObject = watchedObjects.find(objectMap.first);
936 if (findObject != watchedObjects.end())
937 {
938 continue;
939 }
940 // this creates a filter for properties changed for any new probe type
941 auto propertyChange = std::make_unique<dbus::match>(
942 SYSTEM_BUS,
943 "type='signal',member='PropertiesChanged',arg0='" +
944 objectMap.first + "'");
945 auto filter =
946 std::make_shared<dbus::filter>(SYSTEM_BUS, [](dbus::message &m) {
947 auto member = m.get_member();
948 return member == "PropertiesChanged";
949 });
950
951 filter->async_dispatch([&, filter](boost::system::error_code ec,
952 dbus::message) {
953 if (ec)
954 {
955 std::cerr << "register callbacks callback error " << ec << "\n";
956 }
957 propertiesChangedCallback(dbusMatches, threadRunning,
958 systemConfiguration, objServer, filter);
959 });
960 dbusMatches.emplace_back(std::move(propertyChange), filter);
961 }
962}
963
964int main(int argc, char **argv)
965{
966 // setup connection to dbus
967 boost::asio::io_service io;
968 SYSTEM_BUS = std::make_shared<dbus::connection>(io, dbus::bus::system);
969 dbus::DbusObjectServer objServer(SYSTEM_BUS);
970 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
971
972 nlohmann::json systemConfiguration = nlohmann::json::object();
973 rescan(systemConfiguration);
James Feist1b2e2242018-01-30 13:45:19 -0800974 // this line to be removed in future
975 writeJsonFiles(systemConfiguration);
James Feistc95cb142018-02-26 10:41:42 -0800976 unloadAllOverlays();
977 loadOverlays(systemConfiguration);
James Feist75fdeeb2018-02-20 14:26:16 -0800978 postToDbus(systemConfiguration, objServer);
James Feistc95cb142018-02-26 10:41:42 -0800979
James Feist75fdeeb2018-02-20 14:26:16 -0800980 auto object = std::make_shared<dbus::DbusObject>(
981 SYSTEM_BUS, "/xyz/openbmc_project/EntityManager");
982 objServer.register_object(object);
983 auto iface = std::make_shared<dbus::DbusInterface>(
984 "xyz.openbmc_project.EntityManager", SYSTEM_BUS);
985 object->register_interface(iface);
986
987 std::atomic_bool threadRunning(false);
988 // to keep reference to the match / filter objects so they don't get
989 // destroyed
990 std::vector<
991 std::pair<std::unique_ptr<dbus::match>, std::shared_ptr<dbus::filter>>>
992 dbusMatches;
993 iface->register_method("ReScan", [&]() {
994 propertiesChangedCallback(dbusMatches, threadRunning,
995 systemConfiguration, objServer, nullptr);
996 return std::tuple<>(); // this is a bug in boost-dbus, needs some sort
997 // of return
998 });
999 registerCallbacks(dbusMatches, threadRunning, systemConfiguration,
1000 objServer);
1001
James Feist1b2e2242018-01-30 13:45:19 -08001002 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001003
1004 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001005}