Files
sdbus-cpp/src/Object.cpp

407 lines
14 KiB
C++
Raw Normal View History

2017-11-27 14:13:55 +01:00
/**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
2017-11-27 14:13:55 +01:00
*
* @file Object.cpp
*
* 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 "Object.h"
#include "MessageUtils.h"
2017-11-27 14:13:55 +01:00
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/Message.h>
#include <sdbus-c++/Error.h>
#include <sdbus-c++/MethodResult.h>
#include <sdbus-c++/Flags.h>
2017-11-27 14:13:55 +01:00
#include "IConnection.h"
#include "VTableUtils.h"
#include <systemd/sd-bus.h>
#include <utility>
#include <cassert>
2020-02-01 22:47:04 +01:00
namespace sdbus::internal {
2017-11-27 14:13:55 +01:00
Object::Object(sdbus::internal::IConnection& connection, std::string objectPath)
: connection_(connection), objectPath_(std::move(objectPath))
{
}
void Object::registerMethod( const std::string& interfaceName
, std::string methodName
, std::string inputSignature
, std::string outputSignature
, method_callback methodCallback
, Flags flags )
{
registerMethod( interfaceName
, std::move(methodName)
, std::move(inputSignature)
, {}
, std::move(outputSignature)
, {}
, std::move(methodCallback)
, std::move(flags) );
}
void Object::registerMethod( const std::string& interfaceName
, std::string methodName
, std::string inputSignature
, const std::vector<std::string>& inputNames
, std::string outputSignature
, const std::vector<std::string>& outputNames
, method_callback methodCallback
, Flags flags )
2017-11-27 14:13:55 +01:00
{
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
auto& interface = interfaces_[interfaceName];
InterfaceData::MethodData methodData{ std::move(inputSignature)
, std::move(outputSignature)
, paramNamesToString(inputNames) + paramNamesToString(outputNames)
, std::move(methodCallback)
, std::move(flags) };
auto inserted = interface.methods.emplace(std::move(methodName), std::move(methodData)).second;
2017-11-27 14:13:55 +01:00
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
}
void Object::registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, Flags flags )
{
registerSignal(interfaceName, std::move(signalName), std::move(signature), {}, std::move(flags));
}
void Object::registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, const std::vector<std::string>& paramNames
, Flags flags )
2017-11-27 14:13:55 +01:00
{
auto& interface = interfaces_[interfaceName];
InterfaceData::SignalData signalData{std::move(signature), paramNamesToString(paramNames), std::move(flags)};
auto inserted = interface.signals.emplace(std::move(signalName), std::move(signalData)).second;
2017-11-27 14:13:55 +01:00
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal: signal already exists", EINVAL);
}
void Object::registerProperty( const std::string& interfaceName
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, Flags flags )
2017-11-27 14:13:55 +01:00
{
registerProperty( interfaceName
, std::move(propertyName)
, std::move(signature)
, std::move(getCallback)
, {}
, std::move(flags) );
2017-11-27 14:13:55 +01:00
}
void Object::registerProperty( const std::string& interfaceName
, std::string propertyName
, std::string signature
2017-11-27 14:13:55 +01:00
, property_get_callback getCallback
, property_set_callback setCallback
, Flags flags )
2017-11-27 14:13:55 +01:00
{
SDBUS_THROW_ERROR_IF(!getCallback && !setCallback, "Invalid property callbacks provided", EINVAL);
auto& interface = interfaces_[interfaceName];
InterfaceData::PropertyData propertyData{ std::move(signature)
, std::move(getCallback)
, std::move(setCallback)
, std::move(flags)};
auto inserted = interface.properties.emplace(std::move(propertyName), std::move(propertyData)).second;
2017-11-27 14:13:55 +01:00
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register property: property already exists", EINVAL);
}
void Object::setInterfaceFlags(const std::string& interfaceName, Flags flags)
{
auto& interface = interfaces_[interfaceName];
interface.flags = flags;
}
2017-11-27 14:13:55 +01:00
void Object::finishRegistration()
{
for (auto& item : interfaces_)
{
const auto& interfaceName = item.first;
auto& interfaceData = item.second;
2018-05-25 20:48:20 +02:00
const auto& vtable = createInterfaceVTable(interfaceData);
activateInterfaceVTable(interfaceName, interfaceData, vtable);
2017-11-27 14:13:55 +01:00
}
}
void Object::unregister()
{
interfaces_.clear();
removeObjectManager();
}
sdbus::Signal Object::createSignal(const std::string& interfaceName, const std::string& signalName)
2017-11-27 14:13:55 +01:00
{
return connection_.createSignal(objectPath_, interfaceName, signalName);
}
void Object::emitSignal(const sdbus::Signal& message)
2017-11-27 14:13:55 +01:00
{
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid signal message provided", EINVAL);
2017-11-27 14:13:55 +01:00
message.send();
}
void Object::emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames)
{
connection_.emitPropertiesChangedSignal(objectPath_, interfaceName, propNames);
}
void Object::emitPropertiesChangedSignal(const std::string& interfaceName)
{
Object::emitPropertiesChangedSignal(interfaceName, {});
}
void Object::emitInterfacesAddedSignal()
{
connection_.emitInterfacesAddedSignal(objectPath_);
}
void Object::emitInterfacesAddedSignal(const std::vector<std::string>& interfaces)
{
connection_.emitInterfacesAddedSignal(objectPath_, interfaces);
}
void Object::emitInterfacesRemovedSignal()
{
connection_.emitInterfacesRemovedSignal(objectPath_);
}
void Object::emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces)
{
connection_.emitInterfacesRemovedSignal(objectPath_, interfaces);
}
void Object::addObjectManager()
{
objectManagerSlot_ = connection_.addObjectManager(objectPath_);
}
void Object::removeObjectManager()
{
objectManagerSlot_.reset();
}
bool Object::hasObjectManager() const
{
return objectManagerSlot_ != nullptr;
}
sdbus::IConnection& Object::getConnection() const
{
return dynamic_cast<sdbus::IConnection&>(connection_);
}
2018-05-25 20:48:20 +02:00
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
{
auto& vtable = interfaceData.vtable;
2018-05-25 20:48:20 +02:00
assert(vtable.empty());
vtable.push_back(createVTableStartItem(interfaceData.flags.toSdBusInterfaceFlags()));
2018-05-25 20:48:20 +02:00
registerMethodsToVTable(interfaceData, vtable);
registerSignalsToVTable(interfaceData, vtable);
registerPropertiesToVTable(interfaceData, vtable);
vtable.push_back(createVTableEndItem());
return vtable;
}
void Object::registerMethodsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
{
for (const auto& item : interfaceData.methods)
2018-05-25 20:48:20 +02:00
{
const auto& methodName = item.first;
const auto& methodData = item.second;
vtable.push_back(createVTableMethodItem( methodName.c_str()
, methodData.inputArgs.c_str()
, methodData.outputArgs.c_str()
, methodData.paramNames.c_str()
, &Object::sdbus_method_callback
, methodData.flags_.toSdBusMethodFlags() ));
2018-05-25 20:48:20 +02:00
}
}
void Object::registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
{
for (const auto& item : interfaceData.signals)
2018-05-25 20:48:20 +02:00
{
const auto& signalName = item.first;
const auto& signalData = item.second;
vtable.push_back(createVTableSignalItem( signalName.c_str()
, signalData.signature.c_str()
, signalData.paramNames.c_str()
, signalData.flags.toSdBusSignalFlags() ));
2018-05-25 20:48:20 +02:00
}
}
void Object::registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
{
for (const auto& item : interfaceData.properties)
2018-05-25 20:48:20 +02:00
{
const auto& propertyName = item.first;
const auto& propertyData = item.second;
if (!propertyData.setCallback)
2018-05-25 20:48:20 +02:00
vtable.push_back(createVTablePropertyItem( propertyName.c_str()
, propertyData.signature.c_str()
, &Object::sdbus_property_get_callback
, propertyData.flags.toSdBusPropertyFlags() ));
2018-05-25 20:48:20 +02:00
else
vtable.push_back(createVTableWritablePropertyItem( propertyName.c_str()
, propertyData.signature.c_str()
2018-05-25 20:48:20 +02:00
, &Object::sdbus_property_get_callback
, &Object::sdbus_property_set_callback
, propertyData.flags.toSdBusWritablePropertyFlags() ));
2018-05-25 20:48:20 +02:00
}
}
void Object::activateInterfaceVTable( const std::string& interfaceName
, InterfaceData& interfaceData
, const std::vector<sd_bus_vtable>& vtable )
{
interfaceData.slot = connection_.addObjectVTable(objectPath_, interfaceName, &vtable[0], this);
}
std::string Object::paramNamesToString(const std::vector<std::string>& paramNames)
{
std::string names;
for (const auto& name : paramNames)
names += name + '\0';
return names;
2018-05-25 20:48:20 +02:00
}
2017-11-27 14:13:55 +01:00
int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
{
auto* object = static_cast<Object*>(userData);
assert(object != nullptr);
auto message = Message::Factory::create<MethodCall>(sdbusMessage, &object->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 = object->interfaces_[message.getInterfaceName()].methods[message.getMemberName()].callback;
2017-11-27 14:13:55 +01:00
assert(callback);
try
{
callback(std::move(message));
2017-11-27 14:13:55 +01:00
}
catch (const sdbus::Error& e)
{
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
}
return 1;
}
int Object::sdbus_property_get_callback( sd_bus */*bus*/
, const char */*objectPath*/
, const char *interface
, const char *property
, sd_bus_message *sdbusReply
, void *userData
, sd_bus_error *retError )
{
auto* object = static_cast<Object*>(userData);
assert(object != nullptr);
2017-11-27 14:13:55 +01:00
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = object->interfaces_[interface].properties[property].getCallback;
2017-11-27 14:13:55 +01:00
// Getter can be empty - the case of "write-only" property
if (!callback)
{
sd_bus_error_set(retError, "org.freedesktop.DBus.Error.Failed", "Cannot read property as it is write-only");
return 1;
}
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &object->connection_.getSdBusInterface());
2017-11-27 14:13:55 +01:00
try
{
callback(reply);
}
catch (const sdbus::Error& e)
{
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
}
return 1;
}
int Object::sdbus_property_set_callback( sd_bus */*bus*/
, const char */*objectPath*/
, const char *interface
, const char *property
, sd_bus_message *sdbusValue
, void *userData
, sd_bus_error *retError )
{
auto* object = static_cast<Object*>(userData);
assert(object != nullptr);
2017-11-27 14:13:55 +01:00
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = object->interfaces_[interface].properties[property].setCallback;
2017-11-27 14:13:55 +01:00
assert(callback);
auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &object->connection_.getSdBusInterface());
2017-11-27 14:13:55 +01:00
try
{
callback(value);
}
catch (const sdbus::Error& e)
{
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
}
return 1;
}
2020-02-01 22:47:04 +01:00
}
2017-11-27 14:13:55 +01:00
namespace sdbus {
std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath)
{
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::Object>(*sdbusConnection, std::move(objectPath));
}
}