| /* | 
 | // Copyright (c) 2018 Intel Corporation | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //      http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 | */ | 
 |  | 
 | #include <Utils.hpp> | 
 | #include <Overlay.hpp> | 
 | #include <nlohmann/json.hpp> | 
 | #include <fstream> | 
 | #include <regex> | 
 | #include <iostream> | 
 | #include <sdbusplus/asio/connection.hpp> | 
 | #include <sdbusplus/asio/object_server.hpp> | 
 | #include <boost/algorithm/string/case_conv.hpp> | 
 | #include <boost/algorithm/string/predicate.hpp> | 
 | #include <boost/algorithm/string/replace.hpp> | 
 | #include <boost/variant/apply_visitor.hpp> | 
 | #include <boost/lexical_cast.hpp> | 
 | #include <boost/container/flat_map.hpp> | 
 | #include <boost/container/flat_set.hpp> | 
 | #include <VariantVisitors.hpp> | 
 | #include <experimental/filesystem> | 
 |  | 
 | constexpr const char *OUTPUT_DIR = "/var/configuration/"; | 
 | constexpr const char *CONFIGURATION_DIR = "/usr/share/configurations"; | 
 | constexpr const char *TEMPLATE_CHAR = "$"; | 
 | constexpr const size_t PROPERTIES_CHANGED_UNTIL_FLUSH_COUNT = 20; | 
 | constexpr const int32_t MAX_MAPPER_DEPTH = 0; | 
 | constexpr const size_t SLEEP_AFTER_PROPERTIES_CHANGE_SECONDS = 5; | 
 |  | 
 | namespace fs = std::experimental::filesystem; | 
 | struct cmp_str | 
 | { | 
 |     bool operator()(const char *a, const char *b) const | 
 |     { | 
 |         return std::strcmp(a, b) < 0; | 
 |     } | 
 | }; | 
 |  | 
 | struct PerformProbe; | 
 |  | 
 | // underscore T for collison with dbus c api | 
 | enum class probe_type_codes | 
 | { | 
 |     FALSE_T, | 
 |     TRUE_T, | 
 |     AND, | 
 |     OR, | 
 |     FOUND, | 
 |     MATCH_ONE | 
 | }; | 
 | const static boost::container::flat_map<const char *, probe_type_codes, cmp_str> | 
 |     PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T}, | 
 |                  {"TRUE", probe_type_codes::TRUE_T}, | 
 |                  {"AND", probe_type_codes::AND}, | 
 |                  {"OR", probe_type_codes::OR}, | 
 |                  {"FOUND", probe_type_codes::FOUND}, | 
 |                  {"MATCH_ONE", probe_type_codes::MATCH_ONE}}}; | 
 |  | 
 | static constexpr std::array<const char *, 1> SETTABLE_INTERFACES = { | 
 |     "thresholds"}; | 
 |  | 
 | using BasicVariantType = | 
 |     sdbusplus::message::variant<std::string, int64_t, uint64_t, double, int32_t, | 
 |                                 uint32_t, int16_t, uint16_t, uint8_t, bool>; | 
 |  | 
 | using GetSubTreeType = std::vector< | 
 |     std::pair<std::string, | 
 |               std::vector<std::pair<std::string, std::vector<std::string>>>>>; | 
 |  | 
 | using ManagedObjectType = boost::container::flat_map< | 
 |     sdbusplus::message::object_path, | 
 |     boost::container::flat_map< | 
 |         std::string, | 
 |         boost::container::flat_map<std::string, BasicVariantType>>>; | 
 |  | 
 | boost::container::flat_map< | 
 |     std::string, | 
 |     std::vector<boost::container::flat_map<std::string, BasicVariantType>>> | 
 |     DBUS_PROBE_OBJECTS; | 
 | std::vector<std::string> PASSED_PROBES; | 
 |  | 
 | // todo: pass this through nicer | 
 | std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS; | 
 |  | 
 | const std::regex ILLEGAL_DBUS_REGEX("[^A-Za-z0-9_]"); | 
 |  | 
 | void registerCallbacks(boost::asio::io_service &io, | 
 |                        std::vector<sdbusplus::bus::match::match> &dbusMatches, | 
 |                        nlohmann::json &systemConfiguration, | 
 |                        sdbusplus::asio::object_server &objServer); | 
 |  | 
 | // calls the mapper to find all exposed objects of an interface type | 
 | // and creates a vector<flat_map> that contains all the key value pairs | 
 | // getManagedObjects | 
 | void findDbusObjects(std::shared_ptr<PerformProbe> probe, | 
 |                      std::shared_ptr<sdbusplus::asio::connection> connection, | 
 |                      std::string &interface) | 
 | { | 
 |     // todo: this is only static because the mapper is unreliable as of today | 
 |     static boost::container::flat_map<std::string, | 
 |                                       boost::container::flat_set<std::string>> | 
 |         connections; | 
 |  | 
 |     // store reference to pending callbacks so we don't overwhelm services | 
 |     static boost::container::flat_map< | 
 |         std::string, std::vector<std::shared_ptr<PerformProbe>>> | 
 |         pendingProbes; | 
 |  | 
 |     if (DBUS_PROBE_OBJECTS[interface].size()) | 
 |     { | 
 |         return; | 
 |     } | 
 |  | 
 |     // add shared_ptr to vector of Probes waiting for callback from a specific | 
 |     // interface to keep alive while waiting for response | 
 |     std::array<const char *, 1> objects = {interface.c_str()}; | 
 |     std::vector<std::shared_ptr<PerformProbe>> &pending = | 
 |         pendingProbes[interface]; | 
 |     auto iter = pending.emplace(pending.end(), probe); | 
 |     // only allow first call to run to not overwhelm processes | 
 |     if (iter != pending.begin()) | 
 |     { | 
 |         return; | 
 |     } | 
 |  | 
 |     // find all connections in the mapper that expose a specific type | 
 |     connection->async_method_call( | 
 |         [&, connection, interface](boost::system::error_code &ec, | 
 |                                    const GetSubTreeType &interfaceSubtree) { | 
 |             auto &interfaceConnections = connections[interface]; | 
 |             if (ec) | 
 |             { | 
 |                 std::cerr | 
 |                     << "Error communicating to mapper, using cached data if " | 
 |                        "available\n"; | 
 |                 if (interfaceConnections.empty()) | 
 |                 { | 
 |                     return; | 
 |                 } | 
 |             } | 
 |             else | 
 |             { | 
 |                 for (auto &object : interfaceSubtree) | 
 |                 { | 
 |                     for (auto &connPair : object.second) | 
 |                     { | 
 |                         auto insertPair = | 
 |                             interfaceConnections.insert(connPair.first); | 
 |                     } | 
 |                 } | 
 |             } | 
 |             // get managed objects for all interfaces | 
 |             for (const auto &conn : interfaceConnections) | 
 |             { | 
 |                 connection->async_method_call( | 
 |                     [&, conn, | 
 |                      interface](boost::system::error_code &ec, | 
 |                                 const ManagedObjectType &managedInterface) { | 
 |                         if (ec) | 
 |                         { | 
 |                             std::cerr | 
 |                                 << "error getting managed object for device " | 
 |                                 << conn << "\n"; | 
 |                             pendingProbes[interface].clear(); | 
 |                             return; | 
 |                         } | 
 |                         for (auto &interfaceManagedObj : managedInterface) | 
 |                         { | 
 |                             auto ifaceObjFind = | 
 |                                 interfaceManagedObj.second.find(interface); | 
 |                             if (ifaceObjFind != | 
 |                                 interfaceManagedObj.second.end()) | 
 |                             { | 
 |                                 std::vector<boost::container::flat_map< | 
 |                                     std::string, BasicVariantType>> | 
 |                                     &dbusObject = DBUS_PROBE_OBJECTS[interface]; | 
 |                                 dbusObject.emplace_back(ifaceObjFind->second); | 
 |                             } | 
 |                         } | 
 |                         pendingProbes[interface].clear(); | 
 |                     }, | 
 |                     conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager", | 
 |                     "GetManagedObjects"); | 
 |             } | 
 |         }, | 
 |         "xyz.openbmc_project.ObjectMapper", | 
 |         "/xyz/openbmc_project/object_mapper", | 
 |         "xyz.openbmc_project.ObjectMapper", "GetSubTree", "", MAX_MAPPER_DEPTH, | 
 |         objects); | 
 | } | 
 | // probes dbus interface dictionary for a key with a value that matches a regex | 
 | bool probeDbus( | 
 |     const std::string &interface, | 
 |     const std::map<std::string, nlohmann::json> &matches, | 
 |     std::vector<boost::container::flat_map<std::string, BasicVariantType>> | 
 |         &devices, | 
 |     bool &foundProbe) | 
 | { | 
 |     std::vector<boost::container::flat_map<std::string, BasicVariantType>> | 
 |         &dbusObject = DBUS_PROBE_OBJECTS[interface]; | 
 |     if (dbusObject.empty()) | 
 |     { | 
 |         foundProbe = false; | 
 |         return false; | 
 |     } | 
 |     foundProbe = true; | 
 |  | 
 |     bool foundMatch = false; | 
 |     for (auto &device : dbusObject) | 
 |     { | 
 |         bool deviceMatches = true; | 
 |         for (auto &match : matches) | 
 |         { | 
 |             auto deviceValue = device.find(match.first); | 
 |             if (deviceValue != device.end()) | 
 |             { | 
 |                 switch (match.second.type()) | 
 |                 { | 
 |                     case nlohmann::json::value_t::string: | 
 |                     { | 
 |                         std::regex search(match.second.get<std::string>()); | 
 |                         std::smatch match; | 
 |  | 
 |                         // convert value to string respresentation | 
 |                         std::string probeValue = mapbox::util::apply_visitor( | 
 |                             VariantToStringVisitor(), deviceValue->second); | 
 |                         if (!std::regex_search(probeValue, match, search)) | 
 |                         { | 
 |                             deviceMatches = false; | 
 |                             break; | 
 |                         } | 
 |                         break; | 
 |                     } | 
 |                     case nlohmann::json::value_t::boolean: | 
 |                     case nlohmann::json::value_t::number_unsigned: | 
 |                     { | 
 |                         unsigned int probeValue = mapbox::util::apply_visitor( | 
 |                             VariantToUnsignedIntVisitor(), deviceValue->second); | 
 |  | 
 |                         if (probeValue != match.second.get<unsigned int>()) | 
 |                         { | 
 |                             deviceMatches = false; | 
 |                         } | 
 |                         break; | 
 |                     } | 
 |                     case nlohmann::json::value_t::number_integer: | 
 |                     { | 
 |                         int probeValue = mapbox::util::apply_visitor( | 
 |                             VariantToIntVisitor(), deviceValue->second); | 
 |  | 
 |                         if (probeValue != match.second.get<int>()) | 
 |                         { | 
 |                             deviceMatches = false; | 
 |                         } | 
 |                         break; | 
 |                     } | 
 |                     case nlohmann::json::value_t::number_float: | 
 |                     { | 
 |                         float probeValue = mapbox::util::apply_visitor( | 
 |                             VariantToFloatVisitor(), deviceValue->second); | 
 |  | 
 |                         if (probeValue != match.second.get<float>()) | 
 |                         { | 
 |                             deviceMatches = false; | 
 |                         } | 
 |                         break; | 
 |                     } | 
 |                 } | 
 |             } | 
 |             else | 
 |             { | 
 |                 deviceMatches = false; | 
 |                 break; | 
 |             } | 
 |         } | 
 |         if (deviceMatches) | 
 |         { | 
 |             devices.emplace_back( | 
 |                 boost::container::flat_map<std::string, BasicVariantType>( | 
 |                     device)); | 
 |             foundMatch = true; | 
 |             deviceMatches = false; // for next iteration | 
 |         } | 
 |     } | 
 |     return foundMatch; | 
 | } | 
 |  | 
 | // default probe entry point, iterates a list looking for specific types to | 
 | // call specific probe functions | 
 | bool probe( | 
 |     const std::vector<std::string> &probeCommand, | 
 |     std::vector<boost::container::flat_map<std::string, BasicVariantType>> | 
 |         &foundDevs) | 
 | { | 
 |     const static std::regex command(R"(\((.*)\))"); | 
 |     std::smatch match; | 
 |     bool ret = false; | 
 |     bool matchOne = false; | 
 |     bool cur = true; | 
 |     probe_type_codes lastCommand = probe_type_codes::FALSE_T; | 
 |  | 
 |     for (auto &probe : probeCommand) | 
 |     { | 
 |         bool foundProbe = false; | 
 |         boost::container::flat_map<const char *, probe_type_codes, | 
 |                                    cmp_str>::const_iterator probeType; | 
 |  | 
 |         for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end(); | 
 |              probeType++) | 
 |         { | 
 |             if (probe.find(probeType->first) != std::string::npos) | 
 |             { | 
 |                 foundProbe = true; | 
 |                 break; | 
 |             } | 
 |         } | 
 |         if (foundProbe) | 
 |         { | 
 |             switch (probeType->second) | 
 |             { | 
 |                 case probe_type_codes::FALSE_T: | 
 |                 { | 
 |                     return false; | 
 |                     break; | 
 |                 } | 
 |                 case probe_type_codes::TRUE_T: | 
 |                 { | 
 |                     return true; | 
 |                     break; | 
 |                 } | 
 |                 case probe_type_codes::MATCH_ONE: | 
 |                 { | 
 |                     // set current value to last, this probe type shouldn't | 
 |                     // affect the outcome | 
 |                     cur = ret; | 
 |                     matchOne = true; | 
 |                     break; | 
 |                 } | 
 |                 /*case probe_type_codes::AND: | 
 |                   break; | 
 |                 case probe_type_codes::OR: | 
 |                   break; | 
 |                   // these are no-ops until the last command switch | 
 |                   */ | 
 |                 case probe_type_codes::FOUND: | 
 |                 { | 
 |                     if (!std::regex_search(probe, match, command)) | 
 |                     { | 
 |                         std::cerr << "found probe sytax error " << probe | 
 |                                   << "\n"; | 
 |                         return false; | 
 |                     } | 
 |                     std::string commandStr = *(match.begin() + 1); | 
 |                     boost::replace_all(commandStr, "'", ""); | 
 |                     cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(), | 
 |                                      commandStr) != PASSED_PROBES.end()); | 
 |                     break; | 
 |                 } | 
 |             } | 
 |         } | 
 |         // look on dbus for object | 
 |         else | 
 |         { | 
 |             if (!std::regex_search(probe, match, command)) | 
 |             { | 
 |                 std::cerr << "dbus probe sytax error " << probe << "\n"; | 
 |                 return false; | 
 |             } | 
 |             std::string commandStr = *(match.begin() + 1); | 
 |             // convert single ticks and single slashes into legal json | 
 |             boost::replace_all(commandStr, "'", "\""); | 
 |             boost::replace_all(commandStr, R"(\)", R"(\\)"); | 
 |             auto json = nlohmann::json::parse(commandStr, nullptr, false); | 
 |             if (json.is_discarded()) | 
 |             { | 
 |                 std::cerr << "dbus command sytax error " << commandStr << "\n"; | 
 |                 return false; | 
 |             } | 
 |             // we can match any (string, variant) property. (string, string) | 
 |             // does a regex | 
 |             std::map<std::string, nlohmann::json> dbusProbeMap = | 
 |                 json.get<std::map<std::string, nlohmann::json>>(); | 
 |             auto findStart = probe.find("("); | 
 |             if (findStart == std::string::npos) | 
 |             { | 
 |                 return false; | 
 |             } | 
 |             std::string probeInterface = probe.substr(0, findStart); | 
 |             cur = | 
 |                 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe); | 
 |         } | 
 |  | 
 |         // some functions like AND and OR only take affect after the | 
 |         // fact | 
 |         switch (lastCommand) | 
 |         { | 
 |             case probe_type_codes::AND: | 
 |                 ret = cur && ret; | 
 |                 break; | 
 |             case probe_type_codes::OR: | 
 |                 ret = cur || ret; | 
 |                 break; | 
 |             default: | 
 |                 ret = cur; | 
 |                 break; | 
 |         } | 
 |         lastCommand = probeType != PROBE_TYPES.end() | 
 |                           ? probeType->second | 
 |                           : probe_type_codes::FALSE_T; | 
 |  | 
 |         if (!foundProbe) | 
 |         { | 
 |             std::cerr << "Illegal probe type " << probe << "\n"; | 
 |             return false; | 
 |         } | 
 |     } | 
 |  | 
 |     // probe passed, but empty device | 
 |     if (ret && foundDevs.size() == 0) | 
 |     { | 
 |         foundDevs.emplace_back( | 
 |             boost::container::flat_map<std::string, BasicVariantType>()); | 
 |     } | 
 |     if (matchOne && foundDevs.size() > 1) | 
 |     { | 
 |         foundDevs.erase(foundDevs.begin() + 1, foundDevs.end()); | 
 |     } | 
 |     return ret; | 
 | } | 
 | // this class finds the needed dbus fields and on destruction runs the probe | 
 | struct PerformProbe : std::enable_shared_from_this<PerformProbe> | 
 | { | 
 |  | 
 |     PerformProbe( | 
 |         const std::vector<std::string> &probeCommand, | 
 |         std::function<void(std::vector<boost::container::flat_map< | 
 |                                std::string, BasicVariantType>> &)> &&callback) : | 
 |         _probeCommand(probeCommand), | 
 |         _callback(std::move(callback)) | 
 |     { | 
 |     } | 
 |     ~PerformProbe() | 
 |     { | 
 |         if (probe(_probeCommand, _foundDevs)) | 
 |         { | 
 |             _callback(_foundDevs); | 
 |         } | 
 |     } | 
 |     void run() | 
 |     { | 
 |         // parse out dbus probes by discarding other probe types | 
 |         boost::container::flat_map<const char *, probe_type_codes, | 
 |                                    cmp_str>::const_iterator probeType; | 
 |  | 
 |         std::vector<std::string> dbusProbes; | 
 |         for (std::string &probe : _probeCommand) | 
 |         { | 
 |             bool found = false; | 
 |             boost::container::flat_map<const char *, probe_type_codes, | 
 |                                        cmp_str>::const_iterator probeType; | 
 |             for (probeType = PROBE_TYPES.begin(); | 
 |                  probeType != PROBE_TYPES.end(); probeType++) | 
 |             { | 
 |                 if (probe.find(probeType->first) != std::string::npos) | 
 |                 { | 
 |                     found = true; | 
 |                     break; | 
 |                 } | 
 |             } | 
 |             if (found) | 
 |             { | 
 |                 continue; | 
 |             } | 
 |             // syntax requires probe before first open brace | 
 |             auto findStart = probe.find("("); | 
 |             std::string interface = probe.substr(0, findStart); | 
 |  | 
 |             findDbusObjects(shared_from_this(), SYSTEM_BUS, interface); | 
 |         } | 
 |     } | 
 |     std::vector<std::string> _probeCommand; | 
 |     std::function<void( | 
 |         std::vector<boost::container::flat_map<std::string, BasicVariantType>> | 
 |             &)> | 
 |         _callback; | 
 |     std::vector<boost::container::flat_map<std::string, BasicVariantType>> | 
 |         _foundDevs; | 
 | }; | 
 |  | 
 | // writes output files to persist data | 
 | void writeJsonFiles(const nlohmann::json &systemConfiguration) | 
 | { | 
 |     std::experimental::filesystem::create_directory(OUTPUT_DIR); | 
 |     std::ofstream output(std::string(OUTPUT_DIR) + "system.json"); | 
 |     output << systemConfiguration.dump(4); | 
 |     output.close(); | 
 | } | 
 |  | 
 | // template function to add array as dbus property | 
 | template <typename PropertyType> | 
 | void addArrayToDbus(const std::string &name, const nlohmann::json &array, | 
 |                     sdbusplus::asio::dbus_interface *iface) | 
 | { | 
 |     std::vector<PropertyType> values; | 
 |     for (const auto &property : array) | 
 |     { | 
 |         auto ptr = property.get_ptr<const PropertyType *>(); | 
 |         if (ptr != nullptr) | 
 |         { | 
 |             values.emplace_back(*ptr); | 
 |         } | 
 |     } | 
 |     iface->register_property(name, values); | 
 | } | 
 |  | 
 | template <typename JsonType> | 
 | bool SetJsonFromPointer(const std::string &ptrStr, const JsonType &value, | 
 |                         nlohmann::json &systemConfiguration) | 
 | { | 
 |     try | 
 |     { | 
 |         nlohmann::json::json_pointer ptr(ptrStr); | 
 |         nlohmann::json &ref = systemConfiguration[ptr]; | 
 |         ref = value; | 
 |         return true; | 
 |     } | 
 |     catch (const std::out_of_range) | 
 |     { | 
 |         return false; | 
 |     } | 
 | } | 
 | // adds simple json types to interface's properties | 
 | void populateInterfaceFromJson(nlohmann::json &systemConfiguration, | 
 |                                const std::string &jsonPointerPath, | 
 |                                sdbusplus::asio::dbus_interface *iface, | 
 |                                nlohmann::json &dict, | 
 |                                sdbusplus::asio::object_server &objServer, | 
 |                                bool setable = false) | 
 | { | 
 |     for (auto &dictPair : dict.items()) | 
 |     { | 
 |         auto type = dictPair.value().type(); | 
 |         bool array = false; | 
 |         if (dictPair.value().type() == nlohmann::json::value_t::array) | 
 |         { | 
 |             array = true; | 
 |             if (!dictPair.value().size()) | 
 |             { | 
 |                 continue; | 
 |             } | 
 |             type = dictPair.value()[0].type(); | 
 |             bool isLegal = true; | 
 |             for (const auto &arrayItem : dictPair.value()) | 
 |             { | 
 |                 if (arrayItem.type() != type) | 
 |                 { | 
 |                     isLegal = false; | 
 |                     break; | 
 |                 } | 
 |             } | 
 |             if (!isLegal) | 
 |             { | 
 |                 std::cerr << "dbus format error" << dictPair.value() << "\n"; | 
 |                 continue; | 
 |             } | 
 |             if (type == nlohmann::json::value_t::object) | 
 |             { | 
 |                 continue; // handled elsewhere | 
 |             } | 
 |         } | 
 |         std::string key = jsonPointerPath + "/" + dictPair.key(); | 
 |         switch (type) | 
 |         { | 
 |             case (nlohmann::json::value_t::boolean): | 
 |             { | 
 |                 if (array) | 
 |                 { | 
 |                     // todo: array of bool isn't detected correctly by | 
 |                     // sdbusplus, change it to numbers | 
 |                     addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(), | 
 |                                              iface); | 
 |                     break; | 
 |                 } | 
 |                 if (setable) | 
 |                 { | 
 |                     iface->register_property( | 
 |                         std::string(dictPair.key()), | 
 |                         dictPair.value().get<bool>(), | 
 |                         [&, key](const bool &newVal, bool &val) { | 
 |                             val = newVal; | 
 |                             if (!SetJsonFromPointer(key, val, | 
 |                                                     systemConfiguration)) | 
 |                             { | 
 |                                 std::cerr << "error writing json\n"; | 
 |                                 return -1; | 
 |                             } | 
 |                             writeJsonFiles(systemConfiguration); | 
 |                             return 1; | 
 |                         }); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     iface->register_property(std::string(dictPair.key()), | 
 |                                              dictPair.value().get<bool>()); | 
 |                 } | 
 |                 break; | 
 |             } | 
 |             case (nlohmann::json::value_t::number_integer): | 
 |             { | 
 |                 if (array) | 
 |                 { | 
 |                     addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(), | 
 |                                             iface); | 
 |                     break; | 
 |                 } | 
 |                 if (setable) | 
 |                 { | 
 |                     iface->register_property( | 
 |                         std::string(dictPair.key()), | 
 |                         dictPair.value().get<int64_t>(), | 
 |                         [&, key](const int64_t &newVal, int64_t &val) { | 
 |                             val = newVal; | 
 |                             if (!SetJsonFromPointer(key, val, | 
 |                                                     systemConfiguration)) | 
 |                             { | 
 |                                 std::cerr << "error writing json\n"; | 
 |                                 return -1; | 
 |                             } | 
 |                             writeJsonFiles(systemConfiguration); | 
 |                             return 1; | 
 |                         }); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     iface->register_property(std::string(dictPair.key()), | 
 |                                              dictPair.value().get<int64_t>()); | 
 |                 } | 
 |                 break; | 
 |             } | 
 |             case (nlohmann::json::value_t::number_unsigned): | 
 |             { | 
 |                 if (array) | 
 |                 { | 
 |                     addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(), | 
 |                                              iface); | 
 |                     break; | 
 |                 } | 
 |                 if (setable) | 
 |                 { | 
 |                     iface->register_property( | 
 |                         std::string(dictPair.key()), | 
 |                         dictPair.value().get<uint64_t>(), | 
 |                         [&, key](const uint64_t &newVal, uint64_t &val) { | 
 |                             val = newVal; | 
 |                             if (!SetJsonFromPointer(key, val, | 
 |                                                     systemConfiguration)) | 
 |                             { | 
 |                                 std::cerr << "error writing json\n"; | 
 |                                 return -1; | 
 |                             } | 
 |                             writeJsonFiles(systemConfiguration); | 
 |                             return 1; | 
 |                         }); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     iface->register_property(std::string(dictPair.key()), | 
 |                                              dictPair.value().get<uint64_t>()); | 
 |                 } | 
 |                 break; | 
 |             } | 
 |             case (nlohmann::json::value_t::number_float): | 
 |             { | 
 |                 if (array) | 
 |                 { | 
 |                     addArrayToDbus<double>(dictPair.key(), dictPair.value(), | 
 |                                            iface); | 
 |                     break; | 
 |                 } | 
 |                 if (setable) | 
 |                 { | 
 |                     iface->register_property( | 
 |                         std::string(dictPair.key()), | 
 |                         dictPair.value().get<double>(), | 
 |                         [&, key](const double &newVal, double &val) { | 
 |                             val = newVal; | 
 |                             if (!SetJsonFromPointer(key, val, | 
 |                                                     systemConfiguration)) | 
 |                             { | 
 |                                 std::cerr << "error writing json\n"; | 
 |                                 return -1; | 
 |                             } | 
 |                             return 1; | 
 |                         }); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     iface->register_property(std::string(dictPair.key()), | 
 |                                              dictPair.value().get<double>()); | 
 |                 } | 
 |                 break; | 
 |             } | 
 |             case (nlohmann::json::value_t::string): | 
 |             { | 
 |                 if (array) | 
 |                 { | 
 |                     addArrayToDbus<std::string>(dictPair.key(), | 
 |                                                 dictPair.value(), iface); | 
 |                     break; | 
 |                 } | 
 |                 if (setable) | 
 |                 { | 
 |                     iface->register_property( | 
 |                         std::string(dictPair.key()), | 
 |                         dictPair.value().get<std::string>(), | 
 |                         [&, key](const std::string &newVal, std::string &val) { | 
 |                             val = newVal; | 
 |                             if (!SetJsonFromPointer(key, val, | 
 |                                                     systemConfiguration)) | 
 |                             { | 
 |                                 std::cerr << "error writing json\n"; | 
 |                                 return -1; | 
 |                             } | 
 |                             writeJsonFiles(systemConfiguration); | 
 |                             return 1; | 
 |                         }); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     iface->register_property( | 
 |                         std::string(dictPair.key()), | 
 |                         dictPair.value().get<std::string>()); | 
 |                 } | 
 |                 break; | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     iface->initialize(); | 
 | } | 
 |  | 
 | void postToDbus(const nlohmann::json &newConfiguration, | 
 |                 nlohmann::json &systemConfiguration, | 
 |                 sdbusplus::asio::object_server &objServer) | 
 |  | 
 | { | 
 |     // iterate through boards | 
 |     for (auto &boardPair : newConfiguration.items()) | 
 |     { | 
 |         std::string boardKey = boardPair.key(); | 
 |         std::vector<std::string> path; | 
 |         std::string jsonPointerPath = "/" + boardKey; | 
 |         // loop through newConfiguration, but use values from system | 
 |         // configuration to be able to modify via dbus later | 
 |         auto boardValues = systemConfiguration[boardKey]; | 
 |         auto findBoardType = boardValues.find("type"); | 
 |         std::string boardType; | 
 |         if (findBoardType != boardValues.end() && | 
 |             findBoardType->type() == nlohmann::json::value_t::string) | 
 |         { | 
 |             boardType = findBoardType->get<std::string>(); | 
 |             std::regex_replace(boardType.begin(), boardType.begin(), | 
 |                                boardType.end(), ILLEGAL_DBUS_REGEX, "_"); | 
 |         } | 
 |         else | 
 |         { | 
 |             std::cerr << "Unable to find type for " << boardKey | 
 |                       << " reverting to Chassis.\n"; | 
 |             boardType = "Chassis"; | 
 |         } | 
 |         std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType); | 
 |  | 
 |         std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(), | 
 |                            ILLEGAL_DBUS_REGEX, "_"); | 
 |         std::string boardName = "/xyz/openbmc_project/inventory/system/" + | 
 |                                 boardtypeLower + "/" + boardKey; | 
 |  | 
 |         auto inventoryIface = objServer.add_interface( | 
 |             boardName, "xyz.openbmc_project.Inventory.Item"); | 
 |         auto boardIface = objServer.add_interface( | 
 |             boardName, "xyz.openbmc_project.Inventory.Item." + boardType); | 
 |  | 
 |         populateInterfaceFromJson(systemConfiguration, jsonPointerPath, | 
 |                                   boardIface.get(), boardValues, objServer); | 
 |         jsonPointerPath += "/"; | 
 |         // iterate through board properties | 
 |         for (auto &boardField : boardValues.items()) | 
 |         { | 
 |             if (boardField.value().type() == nlohmann::json::value_t::object) | 
 |             { | 
 |                 auto iface = | 
 |                     objServer.add_interface(boardName, boardField.key()); | 
 |                 populateInterfaceFromJson( | 
 |                     systemConfiguration, jsonPointerPath + boardField.key(), | 
 |                     iface.get(), boardField.value(), objServer); | 
 |             } | 
 |         } | 
 |  | 
 |         auto exposes = boardValues.find("exposes"); | 
 |         if (exposes == boardValues.end()) | 
 |         { | 
 |             continue; | 
 |         } | 
 |         // iterate through exposes | 
 |         jsonPointerPath += "exposes/"; | 
 |  | 
 |         // store the board level pointer so we can modify it on the way down | 
 |         std::string jsonPointerPathBoard = jsonPointerPath; | 
 |         size_t exposesIndex = -1; | 
 |         for (auto &item : *exposes) | 
 |         { | 
 |             exposesIndex++; | 
 |             jsonPointerPath = jsonPointerPathBoard; | 
 |             jsonPointerPath += std::to_string(exposesIndex); | 
 |  | 
 |             auto findName = item.find("name"); | 
 |             if (findName == item.end()) | 
 |             { | 
 |                 std::cerr << "cannot find name in field " << item << "\n"; | 
 |                 continue; | 
 |             } | 
 |             auto findStatus = item.find("status"); | 
 |             // if status is not found it is assumed to be status = 'okay' | 
 |             if (findStatus != item.end()) | 
 |             { | 
 |                 if (*findStatus == "disabled") | 
 |                 { | 
 |                     continue; | 
 |                 } | 
 |             } | 
 |             auto findType = item.find("type"); | 
 |             std::string itemType; | 
 |             if (findType != item.end()) | 
 |             { | 
 |                 itemType = findType->get<std::string>(); | 
 |                 std::regex_replace(itemType.begin(), itemType.begin(), | 
 |                                    itemType.end(), ILLEGAL_DBUS_REGEX, "_"); | 
 |             } | 
 |             else | 
 |             { | 
 |                 itemType = "unknown"; | 
 |             } | 
 |             std::string itemName = findName->get<std::string>(); | 
 |             std::regex_replace(itemName.begin(), itemName.begin(), | 
 |                                itemName.end(), ILLEGAL_DBUS_REGEX, "_"); | 
 |             auto itemIface = objServer.add_interface( | 
 |                 boardName + "/" + itemName, | 
 |                 "xyz.openbmc_project.Configuration." + itemType); | 
 |  | 
 |             populateInterfaceFromJson(systemConfiguration, jsonPointerPath, | 
 |                                       itemIface.get(), item, objServer); | 
 |  | 
 |             for (auto &objectPair : item.items()) | 
 |             { | 
 |                 jsonPointerPath = jsonPointerPathBoard + | 
 |                                   std::to_string(exposesIndex) + "/" + | 
 |                                   objectPair.key(); | 
 |                 if (objectPair.value().type() == | 
 |                     nlohmann::json::value_t::object) | 
 |                 { | 
 |                     auto objectIface = objServer.add_interface( | 
 |                         boardName + "/" + itemName, | 
 |                         "xyz.openbmc_project.Configuration." + itemType + "." + | 
 |                             objectPair.key()); | 
 |  | 
 |                     populateInterfaceFromJson( | 
 |                         systemConfiguration, jsonPointerPath, objectIface.get(), | 
 |                         objectPair.value(), objServer); | 
 |                 } | 
 |                 else if (objectPair.value().type() == | 
 |                          nlohmann::json::value_t::array) | 
 |                 { | 
 |                     size_t index = 0; | 
 |                     if (!objectPair.value().size()) | 
 |                     { | 
 |                         continue; | 
 |                     } | 
 |                     bool isLegal = true; | 
 |                     auto type = objectPair.value()[0].type(); | 
 |                     if (type != nlohmann::json::value_t::object) | 
 |                     { | 
 |                         continue; | 
 |                     } | 
 |  | 
 |                     // verify legal json | 
 |                     for (const auto &arrayItem : objectPair.value()) | 
 |                     { | 
 |                         if (arrayItem.type() != type) | 
 |                         { | 
 |                             isLegal = false; | 
 |                             break; | 
 |                         } | 
 |                     } | 
 |                     if (!isLegal) | 
 |                     { | 
 |                         std::cerr << "dbus format error" << objectPair.value() | 
 |                                   << "\n"; | 
 |                         break; | 
 |                     } | 
 |  | 
 |                     for (auto &arrayItem : objectPair.value()) | 
 |                     { | 
 |                         // limit what interfaces accept set for saftey | 
 |                         bool setable = std::find(SETTABLE_INTERFACES.begin(), | 
 |                                                  SETTABLE_INTERFACES.end(), | 
 |                                                  objectPair.key()) != | 
 |                                        SETTABLE_INTERFACES.end(); | 
 |  | 
 |                         auto objectIface = objServer.add_interface( | 
 |                             boardName + "/" + itemName, | 
 |                             "xyz.openbmc_project.Configuration." + itemType + | 
 |                                 "." + objectPair.key() + | 
 |                                 std::to_string(index++)); | 
 |                         populateInterfaceFromJson( | 
 |                             systemConfiguration, | 
 |                             jsonPointerPath + "/" + std::to_string(index), | 
 |                             objectIface.get(), arrayItem, objServer, setable); | 
 |                     } | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | // finds the template character (currently set to $) and replaces the value with | 
 | // the field found in a dbus object i.e. $ADDRESS would get populated with the | 
 | // ADDRESS field from a object on dbus | 
 | void templateCharReplace( | 
 |     nlohmann::json::iterator &keyPair, | 
 |     const boost::container::flat_map<std::string, BasicVariantType> | 
 |         &foundDevice, | 
 |     size_t &foundDeviceIdx) | 
 | { | 
 |     if (keyPair.value().type() == nlohmann::json::value_t::object) | 
 |     { | 
 |         for (auto nextLayer = keyPair.value().begin(); | 
 |              nextLayer != keyPair.value().end(); nextLayer++) | 
 |         { | 
 |             templateCharReplace(nextLayer, foundDevice, foundDeviceIdx); | 
 |         } | 
 |         return; | 
 |     } | 
 |     else if (keyPair.value().type() != nlohmann::json::value_t::string) | 
 |     { | 
 |         return; | 
 |     } | 
 |  | 
 |     std::string value = keyPair.value(); | 
 |     if (value.find(TEMPLATE_CHAR) != std::string::npos) | 
 |     { | 
 |         std::string templateValue = value; | 
 |  | 
 |         templateValue.erase(0, 1); // remove template character | 
 |  | 
 |         // special case index | 
 |         if ("index" == templateValue) | 
 |         { | 
 |             keyPair.value() = foundDeviceIdx; | 
 |         } | 
 |         else | 
 |         { | 
 |             bool found = false; | 
 |             for (auto &foundDevicePair : foundDevice) | 
 |             { | 
 |                 if (boost::iequals(foundDevicePair.first, templateValue)) | 
 |                 { | 
 |                     mapbox::util::apply_visitor( | 
 |                         [&](auto &&val) { keyPair.value() = val; }, | 
 |                         foundDevicePair.second); | 
 |                     found = true; | 
 |                     break; | 
 |                 } | 
 |             } | 
 |             if (!found) | 
 |             { | 
 |                 std::cerr << "could not find symbol " << templateValue << "\n"; | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | // reads json files out of the filesystem | 
 | bool findJsonFiles(std::list<nlohmann::json> &configurations) | 
 | { | 
 |     // find configuration files | 
 |     std::vector<fs::path> jsonPaths; | 
 |     if (!find_files(fs::path(CONFIGURATION_DIR), R"(.*\.json)", jsonPaths, 0)) | 
 |     { | 
 |         std::cerr << "Unable to find any configuration files in " | 
 |                   << CONFIGURATION_DIR << "\n"; | 
 |         return false; | 
 |     } | 
 |     for (auto &jsonPath : jsonPaths) | 
 |     { | 
 |         std::ifstream jsonStream(jsonPath.c_str()); | 
 |         if (!jsonStream.good()) | 
 |         { | 
 |             std::cerr << "unable to open " << jsonPath.string() << "\n"; | 
 |             continue; | 
 |         } | 
 |         auto data = nlohmann::json::parse(jsonStream, nullptr, false); | 
 |         if (data.is_discarded()) | 
 |         { | 
 |             std::cerr << "syntax error in " << jsonPath.string() << "\n"; | 
 |             continue; | 
 |         } | 
 |         if (data.type() == nlohmann::json::value_t::array) | 
 |         { | 
 |             for (auto &d : data) | 
 |             { | 
 |                 configurations.emplace_back(d); | 
 |             } | 
 |         } | 
 |         else | 
 |         { | 
 |             configurations.emplace_back(data); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | struct PerformScan : std::enable_shared_from_this<PerformScan> | 
 | { | 
 |  | 
 |     PerformScan(nlohmann::json &systemConfiguration, | 
 |                 std::list<nlohmann::json> &configurations, | 
 |                 std::function<void(void)> &&callback) : | 
 |         _systemConfiguration(systemConfiguration), | 
 |         _configurations(configurations), _callback(std::move(callback)) | 
 |     { | 
 |     } | 
 |     void run() | 
 |     { | 
 |         for (auto it = _configurations.begin(); it != _configurations.end();) | 
 |         { | 
 |             auto findProbe = it->find("probe"); | 
 |             auto findName = it->find("name"); | 
 |  | 
 |             nlohmann::json probeCommand; | 
 |             // check for poorly formatted fields, probe must be an array | 
 |             if (findProbe == it->end()) | 
 |             { | 
 |                 std::cerr << "configuration file missing probe:\n " << *it | 
 |                           << "\n"; | 
 |                 it = _configurations.erase(it); | 
 |                 continue; | 
 |             } | 
 |             else if ((*findProbe).type() != nlohmann::json::value_t::array) | 
 |             { | 
 |                 probeCommand = nlohmann::json::array(); | 
 |                 probeCommand.push_back(*findProbe); | 
 |             } | 
 |             else | 
 |             { | 
 |                 probeCommand = *findProbe; | 
 |             } | 
 |  | 
 |             if (findName == it->end()) | 
 |             { | 
 |                 std::cerr << "configuration file missing name:\n " << *it | 
 |                           << "\n"; | 
 |                 it = _configurations.erase(it); | 
 |                 continue; | 
 |             } | 
 |             std::string name = *findName; | 
 |  | 
 |             if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(), name) != | 
 |                 PASSED_PROBES.end()) | 
 |             { | 
 |                 it = _configurations.erase(it); | 
 |                 continue; | 
 |             } | 
 |             nlohmann::json *record = &(*it); | 
 |  | 
 |             // store reference to this to children to makes sure we don't get | 
 |             // destroyed too early | 
 |             auto thisRef = shared_from_this(); | 
 |             auto p = std::make_shared<PerformProbe>( | 
 |                 probeCommand, | 
 |                 [&, record, name, | 
 |                  thisRef](std::vector<boost::container::flat_map< | 
 |                               std::string, BasicVariantType>> &foundDevices) { | 
 |                     _passed = true; | 
 |  | 
 |                     PASSED_PROBES.push_back(name); | 
 |                     size_t foundDeviceIdx = 0; | 
 |  | 
 |                     for (auto &foundDevice : foundDevices) | 
 |                     { | 
 |                         for (auto keyPair = record->begin(); | 
 |                              keyPair != record->end(); keyPair++) | 
 |                         { | 
 |                             templateCharReplace(keyPair, foundDevice, | 
 |                                                 foundDeviceIdx); | 
 |                         } | 
 |                         auto findExpose = record->find("exposes"); | 
 |                         if (findExpose == record->end()) | 
 |                         { | 
 |                             continue; | 
 |                         } | 
 |                         for (auto &expose : *findExpose) | 
 |                         { | 
 |                             for (auto keyPair = expose.begin(); | 
 |                                  keyPair != expose.end(); keyPair++) | 
 |                             { | 
 |  | 
 |                                 // fill in template characters with devices | 
 |                                 // found | 
 |                                 templateCharReplace(keyPair, foundDevice, | 
 |                                                     foundDeviceIdx); | 
 |                                 // special case bind | 
 |                                 if (boost::starts_with(keyPair.key(), "bind_")) | 
 |                                 { | 
 |                                     if (keyPair.value().type() != | 
 |                                         nlohmann::json::value_t::string) | 
 |                                     { | 
 |                                         std::cerr << "bind_ value must be of " | 
 |                                                      "type string " | 
 |                                                   << keyPair.key() << "\n"; | 
 |                                         continue; | 
 |                                     } | 
 |                                     bool foundBind = false; | 
 |                                     std::string bind = keyPair.key().substr( | 
 |                                         sizeof("bind_") - 1); | 
 |                                     for (auto &configurationPair : | 
 |                                          _systemConfiguration.items()) | 
 |                                     { | 
 |  | 
 |                                         auto configListFind = | 
 |                                             configurationPair.value().find( | 
 |                                                 "exposes"); | 
 |  | 
 |                                         if (configListFind == | 
 |                                                 configurationPair.value() | 
 |                                                     .end() || | 
 |                                             configListFind->type() != | 
 |                                                 nlohmann::json::value_t::array) | 
 |                                         { | 
 |                                             continue; | 
 |                                         } | 
 |                                         for (auto &exposedObject : | 
 |                                              *configListFind) | 
 |                                         { | 
 |                                             std::string foundObjectName = | 
 |                                                 (exposedObject)["name"]; | 
 |                                             if (boost::iequals( | 
 |                                                     foundObjectName, | 
 |                                                     keyPair.value() | 
 |                                                         .get<std::string>())) | 
 |                                             { | 
 |                                                 exposedObject["status"] = | 
 |                                                     "okay"; | 
 |                                                 expose[bind] = exposedObject; | 
 |  | 
 |                                                 foundBind = true; | 
 |                                                 break; | 
 |                                             } | 
 |                                         } | 
 |                                         if (foundBind) | 
 |                                         { | 
 |                                             break; | 
 |                                         } | 
 |                                     } | 
 |                                     if (!foundBind) | 
 |                                     { | 
 |                                         std::cerr << "configuration file " | 
 |                                                      "dependency error, " | 
 |                                                      "could not find bind " | 
 |                                                   << keyPair.value() << "\n"; | 
 |                                     } | 
 |                                 } | 
 |                             } | 
 |                         } | 
 |                     } | 
 |                     _systemConfiguration[name] = *record; | 
 |                 }); | 
 |             p->run(); | 
 |             it++; | 
 |         } | 
 |     } | 
 |  | 
 |     ~PerformScan() | 
 |     { | 
 |         if (_passed) | 
 |         { | 
 |             auto nextScan = std::make_shared<PerformScan>( | 
 |                 _systemConfiguration, _configurations, std::move(_callback)); | 
 |             nextScan->run(); | 
 |         } | 
 |         else | 
 |         { | 
 |             _callback(); | 
 |         } | 
 |     } | 
 |     nlohmann::json &_systemConfiguration; | 
 |     std::list<nlohmann::json> _configurations; | 
 |     std::function<void(void)> _callback; | 
 |     std::vector<std::shared_ptr<PerformProbe>> _probes; | 
 |     bool _passed = false; | 
 | }; | 
 |  | 
 | // main properties changed entry | 
 | void propertiesChangedCallback( | 
 |     boost::asio::io_service &io, | 
 |     std::vector<sdbusplus::bus::match::match> &dbusMatches, | 
 |     nlohmann::json &systemConfiguration, | 
 |     sdbusplus::asio::object_server &objServer) | 
 | { | 
 |     static boost::asio::deadline_timer timer(io); | 
 |     timer.expires_from_now(boost::posix_time::seconds(1)); | 
 |  | 
 |     // setup an async wait as we normally get flooded with new requests | 
 |     timer.async_wait([&](const boost::system::error_code &ec) { | 
 |         if (ec == boost::asio::error::operation_aborted) | 
 |         { | 
 |             // we were cancelled | 
 |             return; | 
 |         } | 
 |         else if (ec) | 
 |         { | 
 |             std::cerr << "async wait error " << ec << "\n"; | 
 |             return; | 
 |         } | 
 |  | 
 |         nlohmann::json oldConfiguration = systemConfiguration; | 
 |         DBUS_PROBE_OBJECTS.clear(); | 
 |  | 
 |         std::list<nlohmann::json> configurations; | 
 |         if (!findJsonFiles(configurations)) | 
 |         { | 
 |             std::cerr << "cannot find json files\n"; | 
 |             return; | 
 |         } | 
 |  | 
 |         auto perfScan = std::make_shared<PerformScan>( | 
 |             systemConfiguration, configurations, [&, oldConfiguration]() { | 
 |                 nlohmann::json newConfiguration = systemConfiguration; | 
 |                 for (auto it = newConfiguration.begin(); | 
 |                      it != newConfiguration.end();) | 
 |                 { | 
 |                     auto findKey = oldConfiguration.find(it.key()); | 
 |                     if (findKey != oldConfiguration.end()) | 
 |                     { | 
 |                         it = newConfiguration.erase(it); | 
 |                     } | 
 |                     else | 
 |                     { | 
 |                         it++; | 
 |                     } | 
 |                 } | 
 |                 registerCallbacks(io, dbusMatches, systemConfiguration, | 
 |                                   objServer); | 
 |                 io.post([&, newConfiguration]() { | 
 |                     // todo: for now, only add new configurations, | 
 |                     // unload to come later unloadOverlays(); | 
 |                     loadOverlays(newConfiguration); | 
 |                     io.post([&]() { writeJsonFiles(systemConfiguration); }); | 
 |                     io.post([&, newConfiguration]() { | 
 |                         postToDbus(newConfiguration, systemConfiguration, | 
 |                                    objServer); | 
 |                     }); | 
 |                 }); | 
 |             }); | 
 |         perfScan->run(); | 
 |     }); | 
 | } | 
 |  | 
 | void registerCallbacks(boost::asio::io_service &io, | 
 |                        std::vector<sdbusplus::bus::match::match> &dbusMatches, | 
 |                        nlohmann::json &systemConfiguration, | 
 |                        sdbusplus::asio::object_server &objServer) | 
 | { | 
 |     static boost::container::flat_set<std::string> watchedObjects; | 
 |  | 
 |     for (const auto &objectMap : DBUS_PROBE_OBJECTS) | 
 |     { | 
 |         auto findObject = watchedObjects.find(objectMap.first); | 
 |         if (findObject != watchedObjects.end()) | 
 |         { | 
 |             continue; | 
 |         } | 
 |         std::function<void(sdbusplus::message::message & message)> | 
 |             eventHandler = | 
 |  | 
 |                 [&](sdbusplus::message::message &) { | 
 |                     propertiesChangedCallback(io, dbusMatches, | 
 |                                               systemConfiguration, objServer); | 
 |                 }; | 
 |  | 
 |         sdbusplus::bus::match::match match( | 
 |             static_cast<sdbusplus::bus::bus &>(*SYSTEM_BUS), | 
 |             "type='signal',member='PropertiesChanged',arg0='" + | 
 |                 objectMap.first + "'", | 
 |             eventHandler); | 
 |         dbusMatches.emplace_back(std::move(match)); | 
 |     } | 
 | } | 
 |  | 
 | int main(int argc, char **argv) | 
 | { | 
 |     // setup connection to dbus | 
 |     boost::asio::io_service io; | 
 |     SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io); | 
 |     SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager"); | 
 |  | 
 |     sdbusplus::asio::object_server objServer(SYSTEM_BUS); | 
 |  | 
 |     std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface = | 
 |         objServer.add_interface("/xyz/openbmc_project/EntityManager", | 
 |                                 "xyz.openbmc_project.EntityManager"); | 
 |  | 
 |     std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface = | 
 |         objServer.add_interface("/xyz/openbmc_project/inventory", | 
 |                                 "xyz.openbmc_project.Inventory.Manager"); | 
 |  | 
 |     // to keep reference to the match / filter objects so they don't get | 
 |     // destroyed | 
 |     std::vector<sdbusplus::bus::match::match> dbusMatches; | 
 |  | 
 |     nlohmann::json systemConfiguration = nlohmann::json::object(); | 
 |  | 
 |     inventoryIface->register_method( | 
 |         "Notify", [](const boost::container::flat_map< | 
 |                       std::string, | 
 |                       boost::container::flat_map<std::string, BasicVariantType>> | 
 |                          &object) { return; }); | 
 |     inventoryIface->initialize(); | 
 |  | 
 |     io.post([&]() { | 
 |         unloadAllOverlays(); | 
 |         propertiesChangedCallback(io, dbusMatches, systemConfiguration, | 
 |                                   objServer); | 
 |     }); | 
 |  | 
 |     entityIface->register_method("ReScan", [&]() { | 
 |         propertiesChangedCallback(io, dbusMatches, systemConfiguration, | 
 |                                   objServer); | 
 |     }); | 
 |     entityIface->initialize(); | 
 |  | 
 |     io.run(); | 
 |  | 
 |     return 0; | 
 | } |