summaryrefslogtreecommitdiff
path: root/mapperx
diff options
context:
space:
mode:
authorEd Tanous <ed.tanous@intel.com>2018-01-11 20:50:25 +0300
committerEd Tanous <ed.tanous@intel.com>2018-05-04 19:30:03 +0300
commitd592681baeb54ba3e3adc2f4c7a5cf64dcaa978a (patch)
tree5d84d78cd3d72638a80a91e1d2fd5cf06ef652bd /mapperx
parentda6a71d6a0d061d9ef5195b683506794a8300980 (diff)
downloadprovingground-d592681baeb54ba3e3adc2f4c7a5cf64dcaa978a.tar.xz
Implement Mapper in sdbusplus asio
Change-Id: Ie746db4c3ad94d02e4075bcc0e42a58600f52b29
Diffstat (limited to 'mapperx')
-rw-r--r--mapperx/.clang-format84
-rw-r--r--mapperx/CMakeLists.txt77
-rw-r--r--mapperx/CMakeLists.txt.in64
-rw-r--r--mapperx/src/main.cpp502
4 files changed, 727 insertions, 0 deletions
diff --git a/mapperx/.clang-format b/mapperx/.clang-format
new file mode 100644
index 0000000..37469de
--- /dev/null
+++ b/mapperx/.clang-format
@@ -0,0 +1,84 @@
+---
+Language: Cpp
+# BasedOnStyle: LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: false
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: false
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+ AfterClass: true
+ AfterControlStatement: true
+ AfterEnum: true
+ AfterFunction: true
+ AfterNamespace: true
+ AfterObjCDeclaration: true
+ AfterStruct: true
+ AfterUnion: true
+ BeforeCatch: true
+ BeforeElse: true
+ IndentBraces: false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: AfterColon
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: true
+PointerAlignment: Left
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IndentCaseLabels: true
+IndentWidth: 4
+IndentWrappedFunctionNames: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Right
+ReflowComments: true
+SortIncludes: false
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp11
+TabWidth: 4
+UseTab: Never
+...
diff --git a/mapperx/CMakeLists.txt b/mapperx/CMakeLists.txt
new file mode 100644
index 0000000..c70d6cd
--- /dev/null
+++ b/mapperx/CMakeLists.txt
@@ -0,0 +1,77 @@
+cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR)
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
+
+option(YOCTO_DEPENDENCIES "Use YOCTO depedencies system" OFF)
+
+project(mapperx CXX)
+
+# Enable link time optimization This is a temporary workaround because
+# INTERPROCEDURAL_OPTIMIZATION isn't available until cmake 3.9. gcc-ar and gcc-
+# ranlib are wrappers around ar and ranlib which add the lto plugin to the
+# command line.
+if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+ if(NOT CMAKE_BUILD_TYPE MATCHES Debug)
+ STRING(REGEX REPLACE "ar$" "gcc-ar" CMAKE_AR ${CMAKE_AR})
+ STRING(REGEX REPLACE "ranlib$" "gcc-ranlib" CMAKE_RANLIB ${CMAKE_RANLIB})
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto -fno-fat-lto-objects")
+
+ # Reduce the binary size by removing unnecessary dynamic symbol table
+ # entries
+ SET(
+ CMAKE_CXX_FLAGS
+ "${CMAKE_CXX_FLAGS} \
+ -fvisibility=hidden \
+ -fvisibility-inlines-hidden \
+ -Wl,--exclude-libs,ALL"
+ )
+ endif(NOT CMAKE_BUILD_TYPE MATCHES Debug)
+endif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+
+if(NOT ${YOCTO_DEPENDENCIES})
+ file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/prefix)
+ file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/prefix/include)
+ include_directories(${CMAKE_BINARY_DIR}/openbmc-sdbusplus
+ ${CMAKE_BINARY_DIR}/prefix/include)
+ configure_file(CMakeLists.txt.in 3rdparty/CMakeLists.txt)
+ execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3rdparty)
+ execute_process(COMMAND ${CMAKE_COMMAND} --build .
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/3rdparty)
+
+ set(CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR}/prefix ${CMAKE_PREFIX_PATH})
+
+ include_directories(${CMAKE_BINARY_DIR}/sdbusplus-src
+ ${CMAKE_BINARY_DIR}/prefix/include)
+
+ set(WANT_TRANSACTION 0)
+
+ configure_file(${CMAKE_BINARY_DIR}/sdbusplus-src/sdbusplus/server.hpp.in
+ ${CMAKE_BINARY_DIR}/prefix/include/sdbusplus/server.hpp @ONLY)
+ configure_file(${CMAKE_BINARY_DIR}/sdbusplus-src/sdbusplus/bus.hpp.in
+ ${CMAKE_BINARY_DIR}/prefix/include/sdbusplus/bus.hpp @ONLY)
+
+endif()
+
+add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY)
+add_definitions(-DBOOST_SYSTEM_NO_DEPRECATED)
+add_definitions(-DBOOST_ALL_NO_LIB)
+add_definitions(-DBOOST_NO_RTTI)
+add_definitions(-DBOOST_NO_TYPEID)
+
+find_package(Boost 1.66 REQUIRED)
+include_directories(${BOOST_SRC_DIR})
+
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
+
+find_package(tinyxml2 REQUIRED)
+
+set(TEST_FILES tests/mapper_test.cpp)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
+
+add_executable(mapperx src/main.cpp ${SRC_FILES})
+target_link_libraries(mapperx systemd pthread)
+target_link_libraries(mapperx tinyxml2)
+install(TARGETS mapperx DESTINATION bin)
diff --git a/mapperx/CMakeLists.txt.in b/mapperx/CMakeLists.txt.in
new file mode 100644
index 0000000..54d51f1
--- /dev/null
+++ b/mapperx/CMakeLists.txt.in
@@ -0,0 +1,64 @@
+cmake_minimum_required(VERSION 3.5)
+
+include(ExternalProject)
+
+file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/prefix)
+file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/prefix/include)
+
+ExternalProject_Add(
+ sdbusplus
+ GIT_REPOSITORY
+ "ssh://git-amr-2.devtools.intel.com:29418/openbmc-sdbusplus"
+ SOURCE_DIR
+ "${CMAKE_BINARY_DIR}/sdbusplus-src"
+ BINARY_DIR
+ "${CMAKE_BINARY_DIR}/sdbusplus-build"
+ CMAKE_ARGS
+ -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/prefix
+ CONFIGURE_COMMAND
+ ""
+ UPDATE_COMMAND
+ git fetch origin refs/changes/95/127095/1
+ &&
+ git checkout FETCH_HEAD BUILD_COMMAND
+ INSTALL_COMMAND
+ cp
+ -r
+ "${CMAKE_BINARY_DIR}/sdbusplus-src/sdbusplus"
+ "${CMAKE_BINARY_DIR}/prefix/include"
+)
+
+ExternalProject_Add(
+ Boost
+ URL
+ https://dl.bintray.com/boostorg/release/1.66.0/source/boost_1_66_0.tar.gz
+ URL_MD5
+ d275cd85b00022313c171f602db59fc5
+ SOURCE_DIR
+ "${CMAKE_BINARY_DIR}/boost-src"
+ BINARY_DIR
+ "${CMAKE_BINARY_DIR}/boost-build"
+ CONFIGURE_COMMAND
+ ""
+ BUILD_COMMAND
+ ""
+ INSTALL_COMMAND
+ mkdir -p "${CMAKE_BINARY_DIR}/prefix/include/"
+ &&
+ cp -R ${CMAKE_BINARY_DIR}/boost-src/boost ${CMAKE_BINARY_DIR}/prefix/include
+)
+
+ExternalProject_Add(
+ tinyxml2
+ GIT_REPOSITORY
+ "https://github.com/leethomason/tinyxml2.git"
+ GIT_TAG
+ 8c8293ba8969a46947606a93ff0cb5a083aab47a
+ CMAKE_ARGS
+ SOURCE_DIR
+ "${CMAKE_BINARY_DIR}/tinyxml2-src"
+ BINARY_DIR
+ "${CMAKE_BINARY_DIR}/tinyxml2-build"
+ CMAKE_ARGS
+ -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/prefix
+) \ No newline at end of file
diff --git a/mapperx/src/main.cpp b/mapperx/src/main.cpp
new file mode 100644
index 0000000..f2434a3
--- /dev/null
+++ b/mapperx/src/main.cpp
@@ -0,0 +1,502 @@
+#include <tinyxml2.h>
+#include <atomic>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/container/flat_map.hpp>
+#include <boost/container/flat_set.hpp>
+#include <chrono>
+#include <iomanip>
+#include <iostream>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+// interface map type is the underlying datastructure the mapper uses.
+// The 3 levels of map are
+// object paths
+// connection names
+// interface names
+using interface_map_type = boost::container::flat_map<
+ std::string, boost::container::flat_map<
+ std::string, boost::container::flat_set<std::string>>>;
+
+struct InProgressIntrospect
+{
+ std::string process_name;
+ std::chrono::time_point<std::chrono::steady_clock> process_start_time;
+ std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
+ global_start_time;
+};
+
+struct cmp_str
+{
+ bool operator()(const char* a, const char* b) const
+ {
+ return std::strcmp(a, b) < 0;
+ }
+};
+
+static const boost::container::flat_set<const char*, cmp_str>
+ ignored_interfaces{"org.freedesktop.DBus.Introspectable",
+ "org.freedesktop.DBus.Peer",
+ "org.freedesktop.DBus.Properties"};
+
+inline bool should_scan_dbus_interface(const std::string& process_name)
+{
+ return boost::starts_with(process_name, "xyz.openbmc_project.") ||
+ boost::starts_with(process_name, "org.openbmc.") ||
+ boost::starts_with(process_name, "com.intel.");
+}
+
+void do_introspect(sdbusplus::asio::connection* system_bus,
+ std::shared_ptr<InProgressIntrospect> transaction,
+ interface_map_type& interface_map, std::string path)
+{
+ system_bus->async_method_call(
+ [&, transaction, path, system_bus](const boost::system::error_code ec,
+ const std::string& introspect_xml) {
+ if (ec)
+ {
+ std::cerr << "Introspect call failed with error: "
+ << ec.message()
+ << " on process: " << transaction->process_name
+ << " path: " << path << "\n";
+ }
+ else
+ {
+ tinyxml2::XMLDocument doc;
+
+ doc.Parse(introspect_xml.c_str());
+ tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
+ if (pRoot == nullptr)
+ {
+ std::cerr << "XML document did not contain any data\n";
+ }
+ else
+ {
+ tinyxml2::XMLElement* pElement =
+ pRoot->FirstChildElement("node");
+ while (pElement != nullptr)
+ {
+ std::string child_path = pElement->Attribute("name");
+ if (child_path.empty())
+ {
+ continue;
+ }
+ else
+ {
+ std::string parent_path(path);
+ if (parent_path == "/")
+ {
+ parent_path.clear();
+ }
+ do_introspect(system_bus, transaction,
+ interface_map,
+ parent_path + "/" + child_path);
+ }
+ pElement = pElement->NextSiblingElement("node");
+ }
+
+ pElement = pRoot->FirstChildElement("interface");
+ while (pElement != nullptr)
+ {
+ std::string iface_name = pElement->Attribute("name");
+ if (iface_name.empty())
+ {
+ continue;
+ }
+ else
+ {
+ if (ignored_interfaces.find(iface_name.c_str()) ==
+ ignored_interfaces.end())
+ {
+ interface_map[path][transaction->process_name]
+ .emplace(iface_name);
+ }
+ if (iface_name ==
+ "xyz.openbmc_project.Associations")
+ {
+ // get association
+ }
+ }
+ pElement = pElement->NextSiblingElement("interface");
+ }
+ }
+ }
+ // if we're the last outstanding caller for this process
+ if (transaction.use_count() == 1)
+ {
+ // TODO(ed) This signal doesn't get exposed properly in the
+ // introspect right now. Find out how to register signals in
+ // sdbusplus
+ sdbusplus::message::message m = system_bus->new_signal(
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper.Private",
+ "IntrospectionComplete");
+ m.append(transaction->process_name);
+ system_bus->call_noreply(m);
+ std::chrono::duration<float> diff =
+ std::chrono::steady_clock::now() -
+ *transaction->global_start_time;
+ std::cout << std::setw(40) << transaction->process_name
+ << " scan took " << diff.count() << " seconds\n";
+
+ // If we're the last outstanding caller globally, calculate the
+ // time it took
+ if (transaction->global_start_time.use_count() == 1)
+ {
+ diff = std::chrono::steady_clock::now() -
+ *transaction->global_start_time;
+ std::cout << "Total scan took " << diff.count()
+ << " seconds to complete\n";
+ }
+ }
+ },
+ transaction->process_name, path, "org.freedesktop.DBus.Introspectable",
+ "Introspect");
+}
+
+void start_new_introspect(
+ sdbusplus::asio::connection* system_bus, interface_map_type& interface_map,
+ const std::string& process_name,
+ std::shared_ptr<std::chrono::time_point<std::chrono::steady_clock>>
+ global_start_time)
+{
+ auto transaction =
+ std::make_shared<InProgressIntrospect>(InProgressIntrospect{
+ process_name, std::chrono::steady_clock::now(), global_start_time});
+ do_introspect(system_bus, transaction, interface_map, "/");
+}
+
+template <class InputIt1, class InputIt2>
+bool intersect(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
+{
+ while (first1 != last1 && first2 != last2)
+ {
+ if (*first1 < *first2)
+ {
+ ++first1;
+ continue;
+ }
+ if (*first2 < *first1)
+ {
+ ++first2;
+ continue;
+ }
+ return true;
+ }
+ return false;
+}
+
+int main(int argc, char** argv)
+{
+ boost::asio::io_service io;
+ auto system_bus = std::make_shared<sdbusplus::asio::connection>(io);
+ system_bus->request_name("xyz.openbmc_project.ObjectMapperX");
+
+ interface_map_type interface_map;
+
+ std::function<void(sdbusplus::message::message & message)>
+ nameChangeHandler = [&](sdbusplus::message::message& message) {
+ std::string name;
+ std::string old_owner;
+ std::string new_owner;
+
+ message.read(name, old_owner, new_owner);
+
+ if (!new_owner.empty())
+ {
+ auto transaction = std::make_shared<
+ std::chrono::time_point<std::chrono::steady_clock>>(
+ std::chrono::steady_clock::now());
+ if (should_scan_dbus_interface(new_owner))
+ {
+ // New daemon added
+ start_new_introspect(system_bus.get(), interface_map,
+ new_owner, transaction);
+ }
+ }
+ if (!old_owner.empty())
+ {
+ // Connection removed
+ interface_map_type::iterator path_it = interface_map.begin();
+ while (path_it != interface_map.end())
+ {
+ auto connection_it = path_it->second.find(old_owner);
+ if (connection_it != path_it->second.end())
+ {
+ if (connection_it->first == old_owner)
+ {
+ path_it->second.erase(connection_it);
+ break;
+ }
+ }
+ if (path_it->second.empty())
+ {
+ // If the last connection to the object is gone, delete
+ // the top level object
+ path_it = interface_map.erase(path_it);
+ continue;
+ }
+ path_it++;
+ }
+ }
+ else
+ {
+ std::cerr << "ERROR: both new path and old path are empty";
+ }
+ };
+
+ sdbusplus::bus::match::match nameOwnerChanged(
+ *system_bus, sdbusplus::bus::match::rules::nameOwnerChanged(),
+ nameChangeHandler);
+
+ std::function<void(sdbusplus::message::message & message)>
+ interfacesAddedHandler = [&](sdbusplus::message::message& message) {
+ sdbusplus::message::object_path obj_path;
+ std::vector<
+ std::pair<std::string,
+ std::vector<std::pair<
+ std::string, sdbusplus::message::variant<bool>>>>>
+ interfaces_added;
+ message.read(obj_path, interfaces_added);
+ const std::string& obj_str =
+ static_cast<const std::string&>(obj_path);
+ auto iface_list = interface_map[obj_str];
+ for (const std::pair<
+ std::string,
+ std::vector<std::pair<std::string,
+ sdbusplus::message::variant<bool>>>>&
+ interface_pair : interfaces_added)
+ {
+ iface_list[interface_pair.first].emplace(message.get_sender());
+ }
+ };
+
+ sdbusplus::bus::match::match interfacesAdded(
+ *system_bus, sdbusplus::bus::match::rules::interfacesAdded(),
+ interfacesAddedHandler);
+
+ std::function<void(sdbusplus::message::message & message)>
+ interfacesRemovedHandler = [&](sdbusplus::message::message& message) {
+ sdbusplus::message::object_path obj_path;
+ std::vector<std::string> interfaces_removed;
+ message.read(obj_path, interfaces_removed);
+ const std::string& object_path_str =
+ static_cast<const std::string&>(obj_path);
+ auto connection_map = interface_map.find(object_path_str);
+ if (connection_map == interface_map.end())
+ {
+ std::cerr << "Unable to find " << object_path_str
+ << " in map\n";
+ return;
+ }
+
+ const std::string sender = std::string(message.get_sender());
+
+ for (const std::string& interface : interfaces_removed)
+ {
+ auto interface_set = connection_map->second.find(sender);
+ if (interface_set == connection_map->second.end())
+ {
+ std::cerr << "Unable to find " << sender << " in map for "
+ << interface << "\n";
+ continue;
+ }
+
+ interface_set->second.erase(interface);
+ // If this was the last interface on this connection, erase the
+ // connection
+ if (interface_set->second.empty())
+ {
+ connection_map->second.erase(interface_set);
+ }
+ }
+ // If this was the last connection on this object path, erase the
+ // object path
+ if (connection_map->second.empty())
+ {
+ interface_map.erase(connection_map);
+ }
+ };
+
+ sdbusplus::bus::match::match interfacesRemoved(
+ *system_bus, sdbusplus::bus::match::rules::interfacesRemoved(),
+ interfacesRemovedHandler);
+
+ // Set up the object server, and send some objects
+ auto server = sdbusplus::asio::object_server(system_bus);
+
+ std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
+ server.add_interface("/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper");
+
+ iface->register_method(
+ "GetAncestors",
+ [&](const std::string& req_path, std::vector<std::string>& interfaces) {
+
+ std::sort(interfaces.begin(), interfaces.end());
+
+ std::vector<interface_map_type::value_type> ret;
+ for (auto& object_path : interface_map)
+ {
+ auto& this_path = object_path.first;
+ bool add = interfaces.empty();
+ if (boost::starts_with(req_path, this_path))
+ {
+ for (auto& interface_map : object_path.second)
+ {
+ add = intersect(interfaces.begin(), interfaces.end(),
+ interface_map.second.begin(),
+ interface_map.second.end());
+ if (add)
+ {
+ break;
+ }
+ }
+ if (add)
+ {
+ // THis makes a copy. TODO(ed) make sdbusplus allow
+ // vectors of pointers so that this doesn't need to copy
+ // all strings
+ ret.emplace_back(object_path);
+ }
+ }
+ }
+
+ return ret;
+ });
+
+ iface->register_method(
+ "GetObject",
+ [&](const std::string& path, std::vector<std::string>& interfaces) {
+ std::sort(interfaces.begin(), interfaces.end());
+ auto path_ref = interface_map.find(path);
+ bool add = false;
+ if (path_ref != interface_map.end())
+ {
+ add = interfaces.empty();
+ for (auto& interface_map : path_ref->second)
+ {
+ if (intersect(interfaces.begin(), interfaces.end(),
+ interface_map.second.begin(),
+ interface_map.second.end()))
+ {
+ add = true;
+ break;
+ }
+ }
+ }
+ if (add)
+ {
+ return path_ref->second;
+ }
+ return decltype(path_ref->second){};
+ });
+
+ iface->register_method(
+ "GetSubTree", [&](const std::string& req_path, int32_t depth,
+ std::vector<std::string>& interfaces) {
+ std::sort(interfaces.begin(), interfaces.end());
+ std::vector<interface_map_type::value_type> ret;
+
+ for (auto& object_path : interface_map)
+ {
+ auto& this_path = object_path.first;
+ if (boost::starts_with(this_path, req_path))
+ {
+ // count the number of slashes past the search term
+ auto this_depth =
+ std::count(this_path.begin() + req_path.size(),
+ this_path.end(), '/');
+ if (this_depth <= depth)
+ {
+ bool add = interfaces.empty();
+ for (auto& interface_map : object_path.second)
+ {
+ if (intersect(interfaces.begin(), interfaces.end(),
+ interface_map.second.begin(),
+ interface_map.second.end()))
+ {
+ add = true;
+ break;
+ }
+ }
+ if (add)
+ {
+ // todo(ed) this is a copy
+ ret.emplace_back(object_path);
+ }
+ }
+ }
+ }
+ return ret;
+ });
+ iface->register_method(
+ "GetSubTreePaths", [&](const std::string& req_path, int32_t depth,
+ const std::vector<std::string>& interfaces) {
+ std::vector<std::string> ret;
+ for (auto& object_path : interface_map)
+ {
+ auto& this_path = object_path.first;
+ if (boost::starts_with(this_path, req_path))
+ {
+ // count the number of slashes past the search term
+ auto this_depth =
+ std::count(this_path.begin() + req_path.size(),
+ this_path.end(), '/');
+ if (this_depth <= depth)
+ {
+ bool add = interfaces.empty();
+ for (auto& interface_map : object_path.second)
+ {
+ if (intersect(interfaces.begin(), interfaces.end(),
+ interface_map.second.begin(),
+ interface_map.second.end()))
+ {
+ add = true;
+ break;
+ }
+ }
+ if (add)
+ {
+ // TODO(ed) this is a copy
+ ret.emplace_back(this_path);
+ }
+ }
+ }
+ }
+
+ return ret;
+ });
+ iface->initialize();
+
+ // This needs to be done after our io_service is in run, so that the match
+ // creation and name reqest happen before we start introspecting.
+ io.post([&]() {
+ system_bus->async_method_call(
+ [&](const boost::system::error_code ec,
+ const std::vector<std::string>& process_names) {
+ if (ec)
+ {
+ std::cerr << "error getting names: " << ec << "\n";
+ }
+ else
+ {
+ auto global_start_time = std::make_shared<
+ std::chrono::time_point<std::chrono::steady_clock>>(
+ std::chrono::steady_clock::now());
+
+ for (const std::string& process_name : process_names)
+ {
+ if (should_scan_dbus_interface(process_name))
+ {
+ start_new_introspect(system_bus.get(),
+ interface_map, process_name,
+ global_start_time);
+ }
+ }
+ }
+ },
+ "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames");
+ });
+ io.run();
+}