Move entity manager to sdbusplus and remove thread
First step in adding changable thresholds.
Also add in support for arrays of simple types
on dbus. Also remove threads as sdbusplus asio is
not thread safe.
Change-Id: I0ee156d7d49ef9b4c618aa141ffc2a9df1d300a1
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/src/EntityManager.cpp b/src/EntityManager.cpp
index a727693..c237346 100644
--- a/src/EntityManager.cpp
+++ b/src/EntityManager.cpp
@@ -16,11 +16,12 @@
#include <Utils.hpp>
#include <Overlay.hpp>
-#include <dbus/properties.hpp>
#include <nlohmann/json.hpp>
#include <fstream>
-#include <future>
#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>
@@ -28,7 +29,6 @@
#include <boost/lexical_cast.hpp>
#include <boost/container/flat_map.hpp>
#include <boost/container/flat_set.hpp>
-#include <dbus/connection.hpp>
#include <VariantVisitors.hpp>
#include <experimental/filesystem>
@@ -36,7 +36,7 @@
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 size_t MAX_MAPPER_DEPTH = 99;
+constexpr const int32_t MAX_MAPPER_DEPTH = 0;
constexpr const size_t SLEEP_AFTER_PROPERTIES_CHANGE_SECONDS = 5;
namespace fs = std::experimental::filesystem;
@@ -48,6 +48,8 @@
}
};
+struct PerformProbe;
+
// underscore T for collison with dbus c api
enum class probe_type_codes
{
@@ -66,146 +68,149 @@
{"FOUND", probe_type_codes::FOUND},
{"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
+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<
- dbus::object_path,
+ sdbusplus::message::object_path,
boost::container::flat_map<
std::string,
- boost::container::flat_map<std::string, dbus::dbus_variant>>>;
+ boost::container::flat_map<std::string, BasicVariantType>>>;
boost::container::flat_map<
std::string,
- std::vector<boost::container::flat_map<std::string, dbus::dbus_variant>>>
+ 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<dbus::connection> SYSTEM_BUS;
+std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
-std::regex ILLEGAL_DBUS_REGEX("[^A-Za-z0-9_]");
+const std::regex ILLEGAL_DBUS_REGEX("[^A-Za-z0-9_]");
-void registerCallbacks(
- std::vector<std::pair<std::unique_ptr<dbus::match>,
- std::shared_ptr<dbus::filter>>> &dbusMatches,
- nlohmann::json &systemConfiguration, dbus::DbusObjectServer &objServer);
+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
-bool findDbusObjects(
- std::shared_ptr<dbus::connection> connection,
- std::vector<boost::container::flat_map<std::string, dbus::dbus_variant>>
- &interfaceDevices,
- std::string interface)
+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
- static const dbus::endpoint mapper("xyz.openbmc_project.ObjectMapper",
- "/xyz/openbmc_project/object_mapper",
- "xyz.openbmc_project.ObjectMapper",
- "GetSubTree");
- dbus::message getMap = dbus::message::new_call(mapper);
- std::vector<std::string> objects = {interface};
- if (!getMap.pack("", MAX_MAPPER_DEPTH, objects))
- {
- std::cerr << "Pack Failed GetSensorSubtree\n";
- return false;
- }
-
- GetSubTreeType interfaceSubtree;
- size_t retries = 1;
- bool unpackStatus = false;
- // the mapper seems to hang occasionally, not responding, so we give it a
- // timeout and retries
- do
- {
- dbus::message getMapResp =
- connection->send(getMap, std::chrono::seconds(2));
- unpackStatus = getMapResp.unpack(interfaceSubtree);
-
- } while (retries-- && !unpackStatus);
-
- auto &interfaceConnections = connections[interface];
- if (!unpackStatus)
- {
- std::cerr << "Error communicating to mapper, using cached data if "
- "available\n";
- if (interfaceConnections.empty())
- {
- return false;
- }
- }
-
- if (unpackStatus)
- {
- interfaceConnections.clear();
- for (auto &object : interfaceSubtree)
- {
- for (auto &connPair : object.second)
+ connection->async_method_call(
+ [&, connection, interface](boost::system::error_code &ec,
+ const GetSubTreeType &interfaceSubtree) {
+ auto &interfaceConnections = connections[interface];
+ if (ec)
{
- interfaceConnections.insert(connPair.first);
+ std::cerr
+ << "Error communicating to mapper, using cached data if "
+ "available\n";
+ if (interfaceConnections.empty())
+ {
+ return;
+ }
}
- }
- }
- // iterate through the connections, adding creating individual device
- // dictionaries
- for (auto &conn : interfaceConnections)
- {
- auto managedObj =
- dbus::endpoint(conn, "/", "org.freedesktop.DBus.ObjectManager",
- "GetManagedObjects");
- dbus::message getManagedObj = dbus::message::new_call(managedObj);
- ManagedObjectType managedInterface;
- retries = 1;
- unpackStatus = false;
- do
- {
- dbus::message getManagedObjResp = connection->send(getManagedObj);
- unpackStatus = getManagedObjResp.unpack(managedInterface);
- } while (retries-- && !unpackStatus);
-
- if (!unpackStatus)
- {
- std::cerr << "error getting managed object for device " << conn
- << "\n";
- continue;
- }
- for (auto &interfaceManagedObj : managedInterface)
- {
- auto ifaceObjFind = interfaceManagedObj.second.find(interface);
- if (ifaceObjFind != interfaceManagedObj.second.end())
+ else
{
- interfaceDevices.emplace_back(ifaceObjFind->second);
+ for (auto &object : interfaceSubtree)
+ {
+ for (auto &connPair : object.second)
+ {
+ auto insertPair =
+ interfaceConnections.insert(connPair.first);
+ }
+ }
}
- }
- }
- return true;
+ // 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 interface dictionary for a key with a value that matches a regex
+// 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, dbus::dbus_variant>>
+ std::vector<boost::container::flat_map<std::string, BasicVariantType>>
&devices,
bool &foundProbe)
{
- auto &dbusObject = DBUS_PROBE_OBJECTS[interface];
+ std::vector<boost::container::flat_map<std::string, BasicVariantType>>
+ &dbusObject = DBUS_PROBE_OBJECTS[interface];
if (dbusObject.empty())
{
- if (!findDbusObjects(SYSTEM_BUS, dbusObject, interface))
- {
- std::cerr << "Found no dbus objects with interface "
- << interface << "\n";
- foundProbe = false;
- return false;
- }
+ foundProbe = false;
+ return false;
}
foundProbe = true;
@@ -226,11 +231,8 @@
std::smatch match;
// convert value to string respresentation
- std::string probeValue = boost::apply_visitor(
- [](const auto &x) {
- return boost::lexical_cast<std::string>(x);
- },
- deviceValue->second);
+ std::string probeValue = mapbox::util::apply_visitor(
+ VariantToStringVisitor(), deviceValue->second);
if (!std::regex_search(probeValue, match, search))
{
deviceMatches = false;
@@ -241,7 +243,7 @@
case nlohmann::json::value_t::boolean:
case nlohmann::json::value_t::number_unsigned:
{
- unsigned int probeValue = boost::apply_visitor(
+ unsigned int probeValue = mapbox::util::apply_visitor(
VariantToUnsignedIntVisitor(), deviceValue->second);
if (probeValue != match.second.get<unsigned int>())
@@ -252,7 +254,7 @@
}
case nlohmann::json::value_t::number_integer:
{
- int probeValue = boost::apply_visitor(
+ int probeValue = mapbox::util::apply_visitor(
VariantToIntVisitor(), deviceValue->second);
if (probeValue != match.second.get<int>())
@@ -263,7 +265,7 @@
}
case nlohmann::json::value_t::number_float:
{
- float probeValue = boost::apply_visitor(
+ float probeValue = mapbox::util::apply_visitor(
VariantToFloatVisitor(), deviceValue->second);
if (probeValue != match.second.get<float>())
@@ -283,7 +285,7 @@
if (deviceMatches)
{
devices.emplace_back(
- boost::container::flat_map<std::string, dbus::dbus_variant>(
+ boost::container::flat_map<std::string, BasicVariantType>(
device));
foundMatch = true;
deviceMatches = false; // for next iteration
@@ -295,8 +297,8 @@
// 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, dbus::dbus_variant>>
+ const std::vector<std::string> &probeCommand,
+ std::vector<boost::container::flat_map<std::string, BasicVariantType>>
&foundDevs)
{
const static std::regex command(R"(\((.*)\))");
@@ -327,12 +329,12 @@
{
case probe_type_codes::FALSE_T:
{
- return false; // todo, actually evaluate?
+ return false;
break;
}
case probe_type_codes::TRUE_T:
{
- return true; // todo, actually evaluate?
+ return true;
break;
}
case probe_type_codes::MATCH_ONE:
@@ -423,11 +425,10 @@
}
// probe passed, but empty device
- // todo: should this be done in main?
if (ret && foundDevs.size() == 0)
{
foundDevs.emplace_back(
- boost::container::flat_map<std::string, dbus::dbus_variant>());
+ boost::container::flat_map<std::string, BasicVariantType>());
}
if (matchOne && foundDevs.size() > 1)
{
@@ -435,90 +436,223 @@
}
return ret;
}
+// this class finds the needed dbus fields and on destruction runs the probe
+struct PerformProbe : std::enable_shared_from_this<PerformProbe>
+{
-// this function is temporary, no need to have once dbus is solified.
-void writeJsonFiles(nlohmann::json &systemConfiguration)
+ 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();
+}
- auto flat = nlohmann::json::array();
- for (auto &pair : systemConfiguration.items())
+// 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 value = pair.value();
- auto exposes = value.find("exposes");
- if (exposes != value.end())
+ auto ptr = property.get_ptr<const PropertyType *>();
+ if (ptr != nullptr)
{
- for (auto &item : *exposes)
- {
- flat.push_back(item);
- }
+ values.emplace_back(*ptr);
}
}
- output = std::ofstream(std::string(OUTPUT_DIR) + "flattened.json");
- output << flat.dump(4);
- output.close();
+ iface->register_property(name, values);
}
// adds simple json types to interface's properties
-void populateInterfaceFromJson(dbus::DbusInterface *iface, nlohmann::json dict,
- dbus::DbusObjectServer &objServer)
+void populateInterfaceFromJson(const nlohmann::json &systemConfiguration,
+ sdbusplus::asio::dbus_interface *iface,
+ nlohmann::json &dict,
+ sdbusplus::asio::object_server &objServer)
{
- std::vector<std::pair<std::string, dbus::dbus_variant>> properties;
- static size_t flushCount = 0;
-
for (auto &dictPair : dict.items())
{
- switch (dictPair.value().type())
+ 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
+ }
+ }
+ switch (type)
{
case (nlohmann::json::value_t::boolean):
{
- properties.emplace_back(std::string(dictPair.key()),
- dictPair.value().get<bool>());
+ 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;
+ }
+ iface->register_property(
+ std::string(dictPair.key()), dictPair.value().get<bool>(),
+ [&, dictPair](const bool &newVal, bool &val) {
+ val = newVal;
+ return 1;
+ });
break;
}
case (nlohmann::json::value_t::number_integer):
{
- properties.emplace_back(std::string(dictPair.key()),
- dictPair.value().get<int64_t>());
+ if (array)
+ {
+ addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
+ iface);
+ break;
+ }
+ iface->register_property(
+ std::string(dictPair.key()),
+ dictPair.value().get<int64_t>(),
+ [&, dictPair](const int64_t &newVal, int64_t &val) {
+ val = newVal;
+ return 1;
+ });
break;
}
case (nlohmann::json::value_t::number_unsigned):
{
- properties.emplace_back(std::string(dictPair.key()),
- dictPair.value().get<uint64_t>());
+ if (array)
+ {
+ addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
+ iface);
+ break;
+ }
+ iface->register_property(
+ std::string(dictPair.key()),
+ dictPair.value().get<uint64_t>(),
+ [&, dictPair](const uint64_t &newVal, uint64_t &val) {
+ val = newVal;
+ return 1;
+ });
break;
}
case (nlohmann::json::value_t::number_float):
{
- properties.emplace_back(std::string(dictPair.key()),
- dictPair.value().get<float>());
+ if (array)
+ {
+ addArrayToDbus<double>(dictPair.key(), dictPair.value(),
+ iface);
+ break;
+ }
+ iface->register_property(
+ std::string(dictPair.key()), dictPair.value().get<double>(),
+ [&, dictPair](const double &newVal, double &val) {
+ val = newVal;
+ return 1;
+ });
break;
}
case (nlohmann::json::value_t::string):
{
- properties.emplace_back(std::string(dictPair.key()),
- dictPair.value().get<std::string>());
+ if (array)
+ {
+ addArrayToDbus<std::string>(dictPair.key(),
+ dictPair.value(), iface);
+ break;
+ }
+ iface->register_property(
+ std::string(dictPair.key()),
+ dictPair.value().get<std::string>(),
+ [&, dictPair](const std::string &newVal, std::string &val) {
+ val = newVal;
+ return 1;
+ });
break;
}
}
}
- if (!properties.empty())
- {
- iface->set_properties(properties);
- // flush the queue after adding an amount of properties so we don't hang
- if (flushCount++ > PROPERTIES_CHANGED_UNTIL_FLUSH_COUNT)
- {
- objServer.flush();
- flushCount = 0;
- }
- }
+ iface->initialize();
}
void postToDbus(const nlohmann::json &systemConfiguration,
- dbus::DbusObjectServer &objServer)
+ sdbusplus::asio::object_server &objServer)
{
for (auto &boardPair : systemConfiguration.items())
@@ -546,21 +680,22 @@
ILLEGAL_DBUS_REGEX, "_");
std::string boardName = "/xyz/openbmc_project/inventory/system/" +
boardtypeLower + "/" + boardKey;
- auto boardObject = objServer.add_object(boardName);
- auto boardIface =
- boardObject->add_interface("xyz.openbmc_project.Inventory.Item");
+ auto inventoryIface = objServer.add_interface(
+ boardName, "xyz.openbmc_project.Inventory.Item");
+ auto boardIface = objServer.add_interface(
+ boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
- boardObject->add_interface("xyz.openbmc_project.Inventory.Item." +
- boardType);
- populateInterfaceFromJson(boardIface.get(), boardValues, objServer);
+ populateInterfaceFromJson(systemConfiguration, boardIface.get(),
+ boardValues, objServer);
for (auto &boardField : boardValues.items())
{
if (boardField.value().type() == nlohmann::json::value_t::object)
{
- auto iface = boardObject->add_interface(boardField.key());
- populateInterfaceFromJson(iface.get(), boardField.value(),
- objServer);
+ auto iface =
+ objServer.add_interface(boardName, boardField.key());
+ populateInterfaceFromJson(systemConfiguration, iface.get(),
+ boardField.value(), objServer);
}
}
auto exposes = boardValues.find("exposes");
@@ -600,41 +735,66 @@
std::string itemName = findName->get<std::string>();
std::regex_replace(itemName.begin(), itemName.begin(),
itemName.end(), ILLEGAL_DBUS_REGEX, "_");
- auto itemObject = objServer.add_object(boardName + "/" + itemName);
- auto itemIface = itemObject->add_interface(
+ auto itemIface = objServer.add_interface(
+ boardName + "/" + itemName,
"xyz.openbmc_project.Configuration." + itemType);
- populateInterfaceFromJson(itemIface.get(), item, objServer);
+ populateInterfaceFromJson(systemConfiguration, itemIface.get(),
+ item, objServer);
for (auto &objectPair : item.items())
{
if (objectPair.value().type() ==
nlohmann::json::value_t::object)
{
- auto objectIface = itemObject->add_interface(
+ auto objectIface = objServer.add_interface(
+ boardName + "/" + itemName,
"xyz.openbmc_project.Configuration." + itemType + "." +
- objectPair.key());
- populateInterfaceFromJson(objectIface.get(),
+ objectPair.key());
+ populateInterfaceFromJson(systemConfiguration,
+ objectIface.get(),
objectPair.value(), objServer);
}
else if (objectPair.value().type() ==
nlohmann::json::value_t::array)
{
size_t index = 0;
- for (auto &arrayItem : objectPair.value())
+ if (!objectPair.value().size())
{
- if (arrayItem.type() != nlohmann::json::value_t::object)
+ 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)
{
- std::cerr << "dbus format error" << arrayItem
- << "\n";
+ isLegal = false;
break;
}
- auto objectIface = itemObject->add_interface(
+ }
+ if (!isLegal)
+ {
+ std::cerr << "dbus format error" << objectPair.value()
+ << "\n";
+ break;
+ }
+
+ for (auto &arrayItem : objectPair.value())
+ {
+ auto objectIface = objServer.add_interface(
+ boardName + "/" + itemName,
"xyz.openbmc_project.Configuration." + itemType +
- "." + objectPair.key() + "." +
- std::to_string(index));
- index++;
- populateInterfaceFromJson(objectIface.get(), arrayItem,
+ "." + objectPair.key() + "." +
+ std::to_string(index++));
+ populateInterfaceFromJson(systemConfiguration,
+ objectIface.get(), arrayItem,
objServer);
}
}
@@ -648,7 +808,7 @@
// ADDRESS field from a object on dbus
void templateCharReplace(
nlohmann::json::iterator &keyPair,
- const boost::container::flat_map<std::string, dbus::dbus_variant>
+ const boost::container::flat_map<std::string, BasicVariantType>
&foundDevice,
size_t &foundDeviceIdx)
{
@@ -687,11 +847,8 @@
{
// convert value to string
// respresentation
- substitute = boost::apply_visitor(
- [](const auto &x) {
- return boost::lexical_cast<std::string>(x);
- },
- foundDevicePair.second);
+ subsitute = mapbox::util::apply_visitor(
+ VariantToStringVisitor(), foundDevicePair.second);
break;
}
}
@@ -707,7 +864,8 @@
}
}
-bool findJsonFiles(std::vector<nlohmann::json> &configurations)
+// reads json files out of the filesystem
+bool findJsonFiles(std::list<nlohmann::json> &configurations)
{
// find configuration files
std::vector<fs::path> jsonPaths;
@@ -745,53 +903,20 @@
}
}
-bool rescan(nlohmann::json &systemConfiguration)
+struct PerformScan : std::enable_shared_from_this<PerformScan>
{
- std::vector<nlohmann::json> configurations;
- if (!findJsonFiles(configurations))
- {
- false;
- }
- // preprocess already passed configurations and missing fields
- if (systemConfiguration.size())
- {
- for (auto it = configurations.begin(); it != configurations.end();)
- {
- auto findName = it->find("name");
- if (findName == it->end())
- {
- std::cerr << "configuration missing name field " << *it << "\n";
- it = configurations.erase(it);
- continue;
- }
- else if (findName->type() != nlohmann::json::value_t::string)
- {
- std::cerr << "name field must be a string " << *findName
- << "\n";
- it = configurations.erase(it);
- continue;
- }
- auto findAlreadyFound =
- systemConfiguration.find(findName->get<std::string>());
- if (findAlreadyFound != systemConfiguration.end())
- {
- it = configurations.erase(it);
- continue;
- }
- // TODO: add in tags to determine if configuration should be
- // refreshed on AC / DC / Always.
- it++;
- }
- }
- // probe until no probes pass
- bool probePassed = true;
- while (probePassed)
+ PerformScan(nlohmann::json &systemConfiguration,
+ std::list<nlohmann::json> &configurations,
+ std::function<void(void)> &&callback) :
+ _systemConfiguration(systemConfiguration),
+ _configurations(configurations), _callback(std::move(callback))
{
- probePassed = false;
- for (auto it = configurations.begin(); it != configurations.end();)
+ }
+ void run()
+ {
+ for (auto it = _configurations.begin(); it != _configurations.end();)
{
- bool eraseConfig = false;
auto findProbe = it->find("probe");
auto findName = it->find("name");
@@ -801,7 +926,8 @@
{
std::cerr << "configuration file missing probe:\n " << *it
<< "\n";
- eraseConfig = true;
+ it = _configurations.erase(it);
+ continue;
}
else if ((*findProbe).type() != nlohmann::json::value_t::array)
{
@@ -817,144 +943,183 @@
{
std::cerr << "configuration file missing name:\n " << *it
<< "\n";
- eraseConfig = true;
+ it = _configurations.erase(it);
+ continue;
}
+ std::string name = *findName;
- std::vector<
- boost::container::flat_map<std::string, dbus::dbus_variant>>
- foundDevices;
- if (!eraseConfig && probe(probeCommand, foundDevices))
+ if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(), name) !=
+ PASSED_PROBES.end())
{
- eraseConfig = true;
- probePassed = true;
- std::string name = *findName;
- PASSED_PROBES.push_back(name);
+ it = _configurations.erase(it);
+ continue;
+ }
+ nlohmann::json *record = &(*it);
- size_t foundDeviceIdx = 0;
+ // 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;
- for (auto &foundDevice : foundDevices)
- {
- for (auto keyPair = it->begin(); keyPair != it->end();
- keyPair++)
+ PASSED_PROBES.push_back(name);
+ size_t foundDeviceIdx = 0;
+
+ for (auto &foundDevice : foundDevices)
{
- templateCharReplace(keyPair, foundDevice,
- foundDeviceIdx);
- }
- auto findExpose = it->find("exposes");
- if (findExpose == it->end())
- {
- continue;
- }
- for (auto &expose : *findExpose)
- {
- for (auto keyPair = expose.begin();
- keyPair != expose.end(); keyPair++)
+ for (auto keyPair = record->begin();
+ keyPair != record->end(); keyPair++)
{
-
- // fill in template characters with devices
- // found
templateCharReplace(keyPair, foundDevice,
foundDeviceIdx);
- // special case bind
- if (boost::starts_with(keyPair.key(), "bind_"))
+ }
+ auto findExpose = record->find("exposes");
+ if (findExpose == record->end())
+ {
+ continue;
+ }
+ for (auto &expose : *findExpose)
+ {
+ for (auto keyPair = expose.begin();
+ keyPair != expose.end(); keyPair++)
{
- 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)
+ // 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;
}
- for (auto &exposedObject : *configListFind)
+ bool foundBind = false;
+ std::string bind = keyPair.key().substr(
+ sizeof("bind_") - 1);
+ for (auto &configurationPair :
+ _systemConfiguration.items())
{
- std::string foundObjectName =
- (exposedObject)["name"];
- if (boost::iequals(
- foundObjectName,
- keyPair.value()
- .get<std::string>()))
- {
- exposedObject["status"] = "okay";
- expose[bind] = exposedObject;
- foundBind = true;
+ 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)
+ if (!foundBind)
{
- break;
+ std::cerr << "configuration file "
+ "dependency error, "
+ "could not find bind "
+ << keyPair.value() << "\n";
}
}
- if (!foundBind)
- {
- std::cerr << "configuration file "
- "dependency error, "
- "could not find bind "
- << keyPair.value() << "\n";
- }
}
}
}
- }
- systemConfiguration[name] = (*it);
- foundDeviceIdx++;
- }
-
- if (eraseConfig)
- {
- it = configurations.erase(it);
- }
- else
- {
- it++;
- }
+ _systemConfiguration[name] = *record;
+ });
+ p->run();
+ it++;
}
}
-}
-void propertiesChangedCallback(
- std::vector<std::pair<std::unique_ptr<dbus::match>,
- std::shared_ptr<dbus::filter>>> &dbusMatches,
- nlohmann::json &systemConfiguration, dbus::DbusObjectServer &objServer,
- std::shared_ptr<dbus::filter> dbusFilter)
-{
- static std::future<void> future;
- static std::atomic_bool threadRunning(false);
- static std::atomic_bool pendingCallback(false);
- bool notRunning = false;
- if (threadRunning.compare_exchange_strong(notRunning, true))
+ ~PerformScan()
{
- future = std::async(std::launch::async, [&] {
+ 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;
+};
- do
- {
- std::this_thread::sleep_for(std::chrono::seconds(
- SLEEP_AFTER_PROPERTIES_CHANGE_SECONDS));
- auto oldConfiguration = systemConfiguration;
- DBUS_PROBE_OBJECTS.clear();
- pendingCallback = false;
- rescan(systemConfiguration);
- auto newConfiguration = systemConfiguration;
+// 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();)
{
@@ -968,42 +1133,26 @@
it++;
}
}
-
- registerCallbacks(dbusMatches, systemConfiguration, objServer);
- // todo: for now, only add new configurations, unload to come
- // later
- // unloadOverlays();
- loadOverlays(newConfiguration);
- // this line to be removed in future
- writeJsonFiles(systemConfiguration);
- // only post new items to bus for now
- postToDbus(newConfiguration, objServer);
- } while (pendingCallback);
- threadRunning = false;
- });
- }
- else
- {
- pendingCallback = true;
- }
- if (dbusFilter != nullptr)
- {
- dbusFilter->async_dispatch([&, dbusFilter](boost::system::error_code ec,
- dbus::message) {
- if (ec)
- {
- std::cerr << "properties changed callback error " << ec << "\n";
- }
- propertiesChangedCallback(dbusMatches, systemConfiguration,
- objServer, dbusFilter);
- });
- }
+ 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, objServer);
+ });
+ });
+ });
+ perfScan->run();
+ });
}
-void registerCallbacks(
- std::vector<std::pair<std::unique_ptr<dbus::match>,
- std::shared_ptr<dbus::filter>>> &dbusMatches,
- nlohmann::json &systemConfiguration, dbus::DbusObjectServer &objServer)
+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;
@@ -1014,26 +1163,20 @@
{
continue;
}
- // this creates a filter for properties changed for any new probe type
- auto propertyChange = std::make_unique<dbus::match>(
- SYSTEM_BUS, "type='signal',member='PropertiesChanged',arg0='" +
- objectMap.first + "'");
- auto filter =
- std::make_shared<dbus::filter>(SYSTEM_BUS, [](dbus::message &m) {
- auto member = m.get_member();
- return member == "PropertiesChanged";
- });
+ std::function<void(sdbusplus::message::message & message)>
+ eventHandler =
- filter->async_dispatch([&, filter](boost::system::error_code ec,
- dbus::message) {
- if (ec)
- {
- std::cerr << "register callbacks callback error " << ec << "\n";
- }
- propertiesChangedCallback(dbusMatches, systemConfiguration,
- objServer, filter);
- });
- dbusMatches.emplace_back(std::move(propertyChange), filter);
+ [&](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));
}
}
@@ -1041,51 +1184,45 @@
{
// setup connection to dbus
boost::asio::io_service io;
- SYSTEM_BUS = std::make_shared<dbus::connection>(io, dbus::bus::system);
-
- dbus::DbusObjectServer objServer(SYSTEM_BUS);
+ SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
- std::vector<
- std::pair<std::unique_ptr<dbus::match>, std::shared_ptr<dbus::filter>>>
- dbusMatches;
- nlohmann::json systemConfiguration = nlohmann::json::object();
- auto entityIface = std::make_shared<dbus::DbusInterface>(
- "xyz.openbmc_project.EntityManager", SYSTEM_BUS);
- auto inventoryIface = std::make_shared<dbus::DbusInterface>(
- "xyz.openbmc_project.Inventory.Manager", SYSTEM_BUS);
- auto inventoryObject = std::make_shared<dbus::DbusObject>(
- SYSTEM_BUS, "/xyz/openbmc_project/inventory");
- objServer.register_object(inventoryObject);
+ sdbusplus::asio::object_server objServer(SYSTEM_BUS);
- inventoryIface->register_method(
- "Notify",
- [](const boost::container::flat_map<
- std::string,
- boost::container::flat_map<std::string, dbus::dbus_variant>>
- &object) { return std::tuple<>(); });
+ std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
+ objServer.add_interface("/xyz/openbmc_project/EntityManager",
+ "xyz.openbmc_project.EntityManager");
- inventoryObject->register_interface(inventoryIface);
- io.post([&]() {
- unloadAllOverlays();
- propertiesChangedCallback(dbusMatches, systemConfiguration, objServer,
- nullptr);
- auto entityObject = std::make_shared<dbus::DbusObject>(
- SYSTEM_BUS, "/xyz/openbmc_project/EntityManager");
- objServer.register_object(entityObject);
- entityObject->register_interface(entityIface);
-
- });
+ 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(dbusMatches, systemConfiguration, objServer,
- nullptr);
- return std::tuple<>(); // this is a bug in boost-dbus, needs some sort
- // of return
+ propertiesChangedCallback(io, dbusMatches, systemConfiguration,
+ objServer);
});
+ entityIface->initialize();
+
+ objServer.add_manager("/xyz/openbmc_project/inventory");
io.run();
diff --git a/src/FruDevice.cpp b/src/FruDevice.cpp
index 606d4f8..614bf91 100644
--- a/src/FruDevice.cpp
+++ b/src/FruDevice.cpp
@@ -16,12 +16,12 @@
#include <Utils.hpp>
#include <boost/container/flat_map.hpp>
+#include <boost/algorithm/string/predicate.hpp>
#include <ctime>
#include <chrono>
#include <thread>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
-#include <dbus/properties.hpp>
#include <fcntl.h>
#include <fstream>
#include <future>