Files
sdbus-cpp/src/Proxy.cpp

206 lines
7.8 KiB
C++
Raw Normal View History

2017-11-27 14:13:55 +01:00
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file Proxy.cpp
2017-11-27 14:13:55 +01:00
*
* Created on: Nov 8, 2016
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Proxy.h"
2017-11-27 14:13:55 +01:00
#include "IConnection.h"
#include "sdbus-c++/Message.h"
#include "sdbus-c++/IConnection.h"
#include "sdbus-c++/Error.h"
2017-11-27 14:13:55 +01:00
#include <systemd/sd-bus.h>
#include <cassert>
#include <chrono>
#include <thread>
2017-11-27 14:13:55 +01:00
namespace sdbus { namespace internal {
Proxy::Proxy(sdbus::internal::IConnection& connection, std::string destination, std::string objectPath)
2017-11-27 14:13:55 +01:00
: connection_(&connection, [](sdbus::internal::IConnection *){ /* Intentionally left empty */ })
, destination_(std::move(destination))
, objectPath_(std::move(objectPath))
{
// The connection is not ours only, it is managed by the client and we just reference it here,
// so we expect the client to manage the event loop upon this connection themselves.
2017-11-27 14:13:55 +01:00
}
Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, std::string destination
, std::string objectPath )
2017-11-27 14:13:55 +01:00
: connection_(std::move(connection))
, destination_(std::move(destination))
, objectPath_(std::move(objectPath))
{
// The connection is ours only, so we have to manage event loop upon this connection,
// so we get signals, async replies, and other messages from D-Bus.
connection_->enterProcessingLoopAsync();
2017-11-27 14:13:55 +01:00
}
MethodCall Proxy::createMethodCall(const std::string& interfaceName, const std::string& methodName)
2017-11-27 14:13:55 +01:00
{
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
}
AsyncMethodCall Proxy::createAsyncMethodCall(const std::string& interfaceName, const std::string& methodName)
{
return AsyncMethodCall{Proxy::createMethodCall(interfaceName, methodName)};
}
MethodReply Proxy::callMethod(const MethodCall& message)
2017-11-27 14:13:55 +01:00
{
return message.send();
}
void Proxy::callMethod(const AsyncMethodCall& message, async_reply_handler asyncReplyCallback)
{
auto callback = (void*)&Proxy::sdbus_async_reply_handler;
// Allocated userData gets deleted in the sdbus_async_reply_handler
auto userData = new AsyncReplyUserData{*this, std::move(asyncReplyCallback)};
message.send(callback, userData);
}
void Proxy::registerSignalHandler( const std::string& interfaceName
, const std::string& signalName
, signal_handler signalHandler )
2017-11-27 14:13:55 +01:00
{
SDBUS_THROW_ERROR_IF(!signalHandler, "Invalid signal handler provided", EINVAL);
auto& interface = interfaces_[interfaceName];
InterfaceData::SignalData signalData{std::move(signalHandler), nullptr};
auto insertionResult = interface.signals_.emplace(signalName, std::move(signalData));
auto inserted = insertionResult.second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal handler: handler already exists", EINVAL);
}
void Proxy::finishRegistration()
2017-11-27 14:13:55 +01:00
{
registerSignalHandlers(*connection_);
2017-11-27 14:13:55 +01:00
}
void Proxy::registerSignalHandlers(sdbus::internal::IConnection& connection)
2017-11-27 14:13:55 +01:00
{
for (auto& interfaceItem : interfaces_)
{
const auto& interfaceName = interfaceItem.first;
auto& signalsOnInterface = interfaceItem.second.signals_;
for (auto& signalItem : signalsOnInterface)
{
const auto& signalName = signalItem.first;
auto& slot = signalItem.second.slot_;
auto* rawSlotPtr = connection.registerSignalHandler( objectPath_
, interfaceName
, signalName
, &Proxy::sdbus_signal_callback
2017-11-27 14:13:55 +01:00
, this );
slot.reset(rawSlotPtr);
slot.get_deleter() = [&connection](sd_bus_slot *slot){ connection.unregisterSignalHandler(slot); };
2017-11-27 14:13:55 +01:00
}
}
}
int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
{
// We are assuming the ownership of the async reply handler pointer passed here
std::unique_ptr<AsyncReplyUserData> asyncReplyUserData{static_cast<AsyncReplyUserData*>(userData)};
assert(asyncReplyUserData != nullptr);
assert(asyncReplyUserData->callback);
MethodReply message{sdbusMessage, &asyncReplyUserData->proxy.connection_->getSdBusInterface()};
const auto* error = sd_bus_message_get_error(sdbusMessage);
if (error == nullptr)
{
asyncReplyUserData->callback(message, nullptr);
}
else
{
sdbus::Error exception(error->name, error->message);
asyncReplyUserData->callback(message, &exception);
}
}
int Proxy::sdbus_signal_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
2017-11-27 14:13:55 +01:00
{
auto* proxy = static_cast<Proxy*>(userData);
assert(proxy != nullptr);
Signal message{sdbusMessage, &proxy->connection_->getSdBusInterface()};
2017-11-27 14:13:55 +01:00
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = proxy->interfaces_[message.getInterfaceName()].signals_[message.getMemberName()].callback_;
2017-11-27 14:13:55 +01:00
assert(callback);
callback(message);
return 1;
}
}}
namespace sdbus {
std::unique_ptr<sdbus::IProxy> createProxy( IConnection& connection
, std::string destination
, std::string objectPath )
2017-11-27 14:13:55 +01:00
{
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(&connection);
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
return std::make_unique<sdbus::internal::Proxy>( *sdbusConnection
, std::move(destination)
, std::move(objectPath) );
2017-11-27 14:13:55 +01:00
}
std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<IConnection>&& connection
, std::string destination
, std::string objectPath )
{
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
connection.release();
return std::make_unique<sdbus::internal::Proxy>( std::unique_ptr<sdbus::internal::IConnection>(sdbusConnection)
, std::move(destination)
, std::move(objectPath) );
}
std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath )
2017-11-27 14:13:55 +01:00
{
auto connection = sdbus::createConnection();
auto sdbusConnection = std::unique_ptr<sdbus::internal::IConnection>(dynamic_cast<sdbus::internal::IConnection*>(connection.release()));
assert(sdbusConnection != nullptr);
return std::make_unique<sdbus::internal::Proxy>( std::move(sdbusConnection)
, std::move(destination)
, std::move(objectPath) );
2017-11-27 14:13:55 +01:00
}
}