#include "simpledbus/advanced/Proxy.h"
#include <simpledbus/advanced/InterfaceRegistry.h>
#include <simpledbus/base/Exceptions.h>
#include <simpledbus/base/Logging.h>
#include <simpledbus/base/Path.h>
#include <algorithm>
#include <iostream>
#include <simpledbus/interfaces/Introspectable.h>
#include <simpledbus/interfaces/Properties.h>
using namespace SimpleDBus;
Proxy::Proxy(std::shared_ptr<Connection> conn, const std::string& bus_name, const std::string& path)
: _conn(conn), _bus_name(bus_name), _path(path), _valid(true), _registered(false) {}
Proxy::~Proxy() {
unregister_object_path();
on_child_created.unload();
on_signal_received.unload();
}
void Proxy::on_registration() {}
std::shared_ptr<Proxy> Proxy::path_create(const std::string& path) {
return std::make_shared<Proxy>(_conn, _bus_name, path);
}
bool Proxy::valid() const { return _valid; }
void Proxy::invalidate() {
_valid = false;
unregister_object_path();
std::scoped_lock lock(_child_access_mutex);
for (auto& child_data : _children) {
child_data.second->invalidate();
}
}
void Proxy::revalidate() {
_valid = true;
register_object_path();
}
std::string Proxy::path() const { return _path; }
std::string Proxy::bus_name() const { return _bus_name; }
const std::map<std::string, std::shared_ptr<Proxy>>& Proxy::children() { return _children; }
const std::map<std::string, std::shared_ptr<Interface>>& Proxy::interfaces() { return _interfaces; }
void Proxy::register_object_path() {
if (!_registered && _conn &&
_conn->register_object_path(_path, [this](Message& msg) { this->message_handle(msg); })) {
_registered = true;
}
}
void Proxy::unregister_object_path() {
if (_registered && _conn && _conn->unregister_object_path(_path)) {
_registered = false;
}
}
void Proxy::register_introspectable_interface() {
std::scoped_lock lock(_interface_access_mutex);
if (!interface_exists("org.freedesktop.DBus.Introspectable")) {
_interfaces.emplace("org.freedesktop.DBus.Introspectable",
std::make_shared<SimpleDBus::Interfaces::Introspectable>(_conn, shared_from_this()));
}
}
bool Proxy::interface_exists(const std::string& name) {
std::scoped_lock lock(_interface_access_mutex);
return _interfaces.find(name) != _interfaces.end();
}
std::shared_ptr<Interface> Proxy::interface_get(const std::string& name) {
std::scoped_lock lock(_interface_access_mutex);
if (!interface_exists(name)) {
throw Exception::InterfaceNotFoundException(_path, name);
}
return _interfaces[name];
}
size_t Proxy::interfaces_count() {
size_t count = 0;
std::scoped_lock lock(_interface_access_mutex);
for (auto& [iface_name, interface] : _interfaces) {
if (interface->is_loaded()) {
count++;
}
}
return count;
}
void Proxy::interfaces_load(Holder managed_interfaces) {
auto managed_interface = managed_interfaces.get<std::map<std::string, Holder>>();
std::scoped_lock lock(_interface_access_mutex);
for (auto& [iface_name, options] : managed_interface) {
if (!interface_exists(iface_name)) {
if (InterfaceRegistry::getInstance().isRegistered(iface_name)) {
_interfaces.emplace(std::make_pair(iface_name, InterfaceRegistry::getInstance().create(
iface_name, _conn, shared_from_this(), options)));
} else {
LOG_WARN("Interface {} not registered within SimpleDBus", iface_name);
}
} else {
_interfaces[iface_name]->load(options);
}
}
}
void Proxy::interfaces_reload(Holder managed_interfaces) {
std::scoped_lock lock(_interface_access_mutex);
for (auto& [iface_name, interface] : _interfaces) {
interface->unload();
}
interfaces_load(managed_interfaces);
}
void Proxy::interfaces_unload(SimpleDBus::Holder removed_interfaces) {
std::scoped_lock lock(_interface_access_mutex);
for (auto& option : removed_interfaces.get<std::vector<Holder>>()) {
std::string iface_name = option.get<std::string>();
if (interface_exists(iface_name)) {
_interfaces[iface_name]->unload();
}
}
}
bool Proxy::interfaces_loaded() {
std::scoped_lock lock(_interface_access_mutex);
for (auto& [iface_name, interface] : _interfaces) {
if (interface->is_loaded()) {
return true;
}
}
return false;
}
bool Proxy::path_exists(const std::string& path) {
std::scoped_lock lock(_child_access_mutex);
return _children.find(path) != _children.end();
}
std::shared_ptr<Proxy> Proxy::path_get(const std::string& path) {
std::scoped_lock lock(_child_access_mutex);
if (!path_exists(path)) {
throw Exception::PathNotFoundException(_path, path);
}
return _children[path];
}
void Proxy::path_add(const std::string& path, SimpleDBus::Holder managed_interfaces) {
if (!PathUtils::is_descendant(_path, path)) {
return;
}
revalidate();
if (path_exists(path)) {
auto child = path_get(path);
child->revalidate();
child->interfaces_load(managed_interfaces);
return;
}
std::scoped_lock lock(_child_access_mutex);
if (PathUtils::is_child(_path, path)) {
std::shared_ptr<Proxy> child = path_create(path);
child->interfaces_load(managed_interfaces);
_children.emplace(std::make_pair(path, child));
on_child_created(path);
} else {
auto child_result = std::find_if(
_children.begin(), _children.end(),
[path](const std::pair<std::string, std::shared_ptr<Proxy>>& child_data) -> bool {
return PathUtils::is_descendant(child_data.first, path);
});
if (child_result != _children.end()) {
child_result->second->path_add(path, managed_interfaces);
} else {
std::string child_path = PathUtils::next_child(_path, path);
std::shared_ptr<Proxy> child = path_create(child_path);
_children.emplace(std::make_pair(child_path, child));
child->path_add(path, managed_interfaces);
on_child_created(child_path);
}
}
}
bool Proxy::path_remove(const std::string& path, SimpleDBus::Holder options) {
if (path == _path) {
invalidate();
interfaces_unload(options);
return path_prune();
}
if (!PathUtils::is_descendant(_path, path)) {
return false;
}
std::scoped_lock lock(_child_access_mutex);
std::string child_path = PathUtils::next_child(_path, path);
if (path_exists(child_path)) {
bool must_erase = _children.at(child_path)->path_remove(path, options);
if (must_erase && _children.at(child_path).use_count() == 1) {
_children.erase(child_path);
}
}
return false;
}
bool Proxy::path_prune() {
std::scoped_lock lock(_child_access_mutex);
std::vector<std::string> to_remove;
for (auto& [child_path, child] : _children) {
if (child->path_prune() && _children.at(child_path).use_count() == 1) {
to_remove.push_back(child_path);
}
}
for (auto& child_path : to_remove) {
_children.erase(child_path);
}
if (_children.empty() && !interfaces_loaded()) {
return true;
}
return false;
}
Holder Proxy::path_collect() {
SimpleDBus::Holder result = SimpleDBus::Holder::create<std::map<std::string, Holder>>();
SimpleDBus::Holder interfaces = SimpleDBus::Holder::create<std::map<std::string, Holder>>();
for (const auto& [interface_name, interface_ptr] : _interfaces) {
SimpleDBus::Holder properties = interface_ptr->handle_property_get_all();
interfaces.dict_append(SimpleDBus::Holder::Type::STRING, interface_name, std::move(properties));
}
if (!interfaces.get<std::map<std::string, Holder>>().empty()) {
result.dict_append(SimpleDBus::Holder::Type::OBJ_PATH, _path, std::move(interfaces));
}
for (const auto& [child_path, child] : _children) {
SimpleDBus::Holder child_result = child->path_collect();
for (auto&& [path, child_interfaces] : child_result.get<std::map<ObjectPath, Holder>>()) {
result.dict_append(SimpleDBus::Holder::Type::OBJ_PATH, std::move(path), std::move(child_interfaces));
}
}
return std::move(result);
}
void Proxy::path_append_child(const std::string& path, std::shared_ptr<Proxy> child) {
if (!PathUtils::is_child(_path, path)) {
return;
}
std::scoped_lock lock(_child_access_mutex);
_children.emplace(std::make_pair(path, child));
}
void Proxy::path_remove_child(const std::string& path) {
if (!PathUtils::is_child(_path, path)) {
return;
}
std::scoped_lock lock(_child_access_mutex);
_children.erase(path);
}
void Proxy::message_handle(Message& msg) {
bool handled = false;
if (interface_exists(msg.get_interface())) {
interface_get(msg.get_interface())->message_handle(msg);
handled = true;
} else {
LOG_WARN("Unhandled message for interface {}: {}", msg.get_interface(), msg.to_string());
if (msg.get_type() == Message::Type::METHOD_CALL) {
Message reply = Message::create_error(msg, "org.freedesktop.DBus.Error.UnknownInterface",
"Unknown interface: " + msg.get_interface());
_conn->send(reply);
handled = true;
}
}
if (msg.get_type() == Message::Type::SIGNAL) {
on_signal_received();
}
if (!handled) {
LOG_ERROR("Unhandled message: {}", msg.to_string());
}
}