2017-11-27 14:13:55 +01:00
|
|
|
/**
|
|
|
|
|
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
|
|
|
|
*
|
|
|
|
|
* @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 <sdbus-c++/IConnection.h>
|
|
|
|
|
#include <sdbus-c++/Message.h>
|
|
|
|
|
#include <sdbus-c++/Error.h>
|
2018-07-02 11:22:00 +02:00
|
|
|
#include <sdbus-c++/MethodResult.h>
|
2019-01-10 08:47:59 +01:00
|
|
|
#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>
|
|
|
|
|
|
|
|
|
|
namespace sdbus { namespace internal {
|
|
|
|
|
|
|
|
|
|
Object::Object(sdbus::internal::IConnection& connection, std::string objectPath)
|
|
|
|
|
: connection_(connection), objectPath_(std::move(objectPath))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Object::registerMethod( const std::string& interfaceName
|
|
|
|
|
, const std::string& methodName
|
|
|
|
|
, const std::string& inputSignature
|
|
|
|
|
, const std::string& outputSignature
|
2019-01-10 08:47:59 +01:00
|
|
|
, method_callback methodCallback
|
|
|
|
|
, Flags flags )
|
2017-11-27 14:13:55 +01:00
|
|
|
{
|
|
|
|
|
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
|
|
|
|
|
|
2018-07-02 11:22:00 +02:00
|
|
|
auto& interface = interfaces_[interfaceName];
|
2019-04-03 00:05:20 +02:00
|
|
|
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(methodCallback), flags};
|
2017-11-27 14:13:55 +01:00
|
|
|
auto inserted = interface.methods_.emplace(methodName, std::move(methodData)).second;
|
|
|
|
|
|
|
|
|
|
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Object::registerSignal( const std::string& interfaceName
|
|
|
|
|
, const std::string& signalName
|
2019-01-10 08:47:59 +01:00
|
|
|
, const std::string& signature
|
|
|
|
|
, Flags flags )
|
2017-11-27 14:13:55 +01:00
|
|
|
{
|
|
|
|
|
auto& interface = interfaces_[interfaceName];
|
|
|
|
|
|
2019-01-10 08:47:59 +01:00
|
|
|
InterfaceData::SignalData signalData{signature, flags};
|
2017-11-27 14:13:55 +01:00
|
|
|
auto inserted = interface.signals_.emplace(signalName, std::move(signalData)).second;
|
|
|
|
|
|
|
|
|
|
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal: signal already exists", EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Object::registerProperty( const std::string& interfaceName
|
|
|
|
|
, const std::string& propertyName
|
|
|
|
|
, const std::string& signature
|
2019-01-10 08:47:59 +01:00
|
|
|
, property_get_callback getCallback
|
|
|
|
|
, Flags flags )
|
2017-11-27 14:13:55 +01:00
|
|
|
{
|
|
|
|
|
registerProperty( interfaceName
|
|
|
|
|
, propertyName
|
|
|
|
|
, signature
|
|
|
|
|
, getCallback
|
2019-01-10 08:47:59 +01:00
|
|
|
, property_set_callback{}
|
|
|
|
|
, flags );
|
2017-11-27 14:13:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Object::registerProperty( const std::string& interfaceName
|
|
|
|
|
, const std::string& propertyName
|
|
|
|
|
, const std::string& signature
|
|
|
|
|
, property_get_callback getCallback
|
2019-01-10 08:47:59 +01:00
|
|
|
, 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];
|
|
|
|
|
|
2019-01-10 08:47:59 +01:00
|
|
|
InterfaceData::PropertyData propertyData{signature, std::move(getCallback), std::move(setCallback), flags};
|
2017-11-27 14:13:55 +01:00
|
|
|
auto inserted = interface.properties_.emplace(propertyName, std::move(propertyData)).second;
|
|
|
|
|
|
|
|
|
|
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register property: property already exists", EINVAL);
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-10 08:47:59 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-02 11:22:00 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-02 11:22:00 +02:00
|
|
|
void Object::emitSignal(const sdbus::Signal& message)
|
2017-11-27 14:13:55 +01:00
|
|
|
{
|
|
|
|
|
message.send();
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-04 20:39:03 +02:00
|
|
|
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_;
|
|
|
|
|
assert(vtable.empty());
|
|
|
|
|
|
2019-01-10 08:47:59 +01:00
|
|
|
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_)
|
|
|
|
|
{
|
|
|
|
|
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()
|
2019-01-10 08:47:59 +01:00
|
|
|
, &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_)
|
|
|
|
|
{
|
|
|
|
|
const auto& signalName = item.first;
|
|
|
|
|
const auto& signalData = item.second;
|
|
|
|
|
|
|
|
|
|
vtable.push_back(createVTableSignalItem( signalName.c_str()
|
2019-01-10 08:47:59 +01:00
|
|
|
, signalData.signature_.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_)
|
|
|
|
|
{
|
|
|
|
|
const auto& propertyName = item.first;
|
|
|
|
|
const auto& propertyData = item.second;
|
|
|
|
|
|
|
|
|
|
if (!propertyData.setCallback_)
|
|
|
|
|
vtable.push_back(createVTablePropertyItem( propertyName.c_str()
|
|
|
|
|
, propertyData.signature_.c_str()
|
2019-01-10 08:47:59 +01:00
|
|
|
, &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()
|
|
|
|
|
, &Object::sdbus_property_get_callback
|
2019-01-10 08:47:59 +01:00
|
|
|
, &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 )
|
|
|
|
|
{
|
|
|
|
|
// Tell, don't ask
|
2019-03-19 20:11:18 +01:00
|
|
|
auto slot = connection_.addObjectVTable(objectPath_, interfaceName, &vtable[0], this);
|
2018-05-25 20:48:20 +02:00
|
|
|
interfaceData.slot_.reset(slot);
|
2019-03-19 20:11:18 +01:00
|
|
|
interfaceData.slot_.get_deleter() = [this](sd_bus_slot *slot){ connection_.removeObjectVTable(slot); };
|
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);
|
2019-03-25 16:28:31 +01:00
|
|
|
assert(object != nullptr);
|
|
|
|
|
|
|
|
|
|
MethodCall message{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_;
|
|
|
|
|
assert(callback);
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2019-04-03 00:05:20 +02:00
|
|
|
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);
|
2019-03-25 16:28:31 +01:00
|
|
|
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_;
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-25 16:28:31 +01:00
|
|
|
Message reply{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);
|
2019-03-25 16:28:31 +01:00
|
|
|
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_;
|
|
|
|
|
assert(callback);
|
|
|
|
|
|
2019-03-25 16:28:31 +01:00
|
|
|
Message value{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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|