Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error when implementing DBus ObjectManager #50

Closed
mdw87 opened this issue May 8, 2019 · 7 comments
Closed

Error when implementing DBus ObjectManager #50

mdw87 opened this issue May 8, 2019 · 7 comments

Comments

@mdw87
Copy link

mdw87 commented May 8, 2019

I am attempting to implement the org.freedesktop.DBus.ObjectManager interface, as defined here.

Through introspection of other DBus services, I was able to produce an XML interface definition for the ObjectManager interface. The ObjectManager requires a method GetManagedObjects with the argument signature a{oa{sa{sv}}}, and emits two signals for InterfacesAdded and InterfacesRemoved.

The XML I generated looks like this:

<node>
	<interface name="org.freedesktop.DBus.ObjectManager">
		<method name="GetManagedObjects">
			<arg type="a{oa{sa{sv}}}" name="object_paths_interfaces_and_properties" direction="out"/>
		</method>
		<signal name="InterfacesAdded">
			<arg type="o" name="object_path"/>
			<arg type="a{sa{sv}}" name="interfaces_and_properties"/>
		</signal>
		<signal name="InterfacesRemoved">
			<arg type="o" name="object_path"/>
			<arg type="as" name="interfaces"/>
		</signal>
	</interface>
</node>

The problem I am having is that when I go to create this service and register with DBus, I am getting the following error:

terminate called after throwing an instance of 'sdbus::Error'
  what():  [org.freedesktop.DBus.Error.InvalidArgs] Failed to register object vtable (Invalid argument)

So, it seems that for some reason my implementation is not valid according to the DBus ObjectManager interface. I have not been able to figure out why. If I change my code to use an interface name other than "org.freedesktop.DBus.ObjectManager", it works fine, so it seems that there is some kind of validation going on when I try to register something as an ObjectManager. I have not been able to figure out where my DBus service differs from the spec.

I have copied below the code from 3 files: the source code of the simple application I am trying to run, the class I created for the ObjectManager adapter, and the auto-generated adapter glue code that was generated using the sdbus-c++-xml2cpp` tool.

I have also tried manually registering the interface, using gattService->registerMethod(interfaceName, "GetManagedObjects", "", "a{oa{sa{sv}}}", &GetManagedObjects) rather than the stubs generated from XML, but I still got the same error.

Any help is greatly appreciated!

Source code of simple-gatt-service.cpp

#include "interfaces/objectmanager_adapter.h"
#include <vector>
#include <string>
#include <iostream>
#include <unistd.h>


int main(int argc, char *argv[])
{
    // Create D-Bus connection to the system bus and requests name on it.
    const char* serviceName = "org.gattservice";
    auto connection = sdbus::createSystemBusConnection(serviceName);

    // Create D-Bus object.
    const char* objectPath = "/org/gattservice";
    
    auto gattService = sdbus::createObject(*connection, objectPath);
    ObjectManagerAdapter objectManager(*connection, objectPath);

    connection->enterProcessingLoop();
}

Source code of objectmanager_adapter.h

#include "objectmanager_adapter_glue.h"

using ObjectManagerAdapterInterface = sdbus::AdaptorInterfaces<org::freedesktop::DBus::ObjectManager_adaptor>;

class ObjectManagerAdapter : public ObjectManagerAdapterInterface
{
public:
    ObjectManagerAdapter(sdbus::IConnection& connection, std::string objectPath)
        : ObjectManagerAdapterInterface(connection, objectPath)
    {
        registerAdaptor();
    }

    ~ObjectManagerAdapter()
    {
        unregisterAdaptor();
    }
private:
    std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> GetManagedObjects() override
    {
        std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> mapOfObjects;

        return {mapOfObjects};
    }
};

Source code of objectmanager_adapter_glue.h (auto-generated from xml)

/*
 * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
 */

#ifndef __sdbuscpp__objectmanager_adapter_glue_h__adaptor__H__
#define __sdbuscpp__objectmanager_adapter_glue_h__adaptor__H__

#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>

namespace org {
namespace freedesktop {
namespace DBus {

class ObjectManager_adaptor
{
public:
    static constexpr const char* interfaceName = "org.freedesktop.DBus.ObjectManager";

protected:
    ObjectManager_adaptor(sdbus::IObject& object)
        : object_(object)
    {
        object_.registerMethod("GetManagedObjects").onInterface(interfaceName).implementedAs([this](){ return this->GetManagedObjects(); });
        object_.registerSignal("InterfacesAdded").onInterface(interfaceName).withParameters<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>>();
        object_.registerSignal("InterfacesRemoved").onInterface(interfaceName).withParameters<sdbus::ObjectPath, std::vector<std::string>>();
    }

public:
    void InterfacesAdded(const sdbus::ObjectPath& object_path, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfaces_and_properties)
    {
        object_.emitSignal("InterfacesAdded").onInterface(interfaceName).withArguments(object_path, interfaces_and_properties);
    }

    void InterfacesRemoved(const sdbus::ObjectPath& object_path, const std::vector<std::string>& interfaces)
    {
        object_.emitSignal("InterfacesRemoved").onInterface(interfaceName).withArguments(object_path, interfaces);
    }

private:
    virtual std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> GetManagedObjects() = 0;

private:
    sdbus::IObject& object_;
};

}}} // namespaces

#endif
@sangelovic
Copy link
Collaborator

sangelovic commented May 8, 2019 via email

@mdw87
Copy link
Author

mdw87 commented May 8, 2019

Oh wow, that makes my job easier :) Thanks!

@sangelovic
Copy link
Collaborator

sangelovic commented May 8, 2019

I've just added a subsection on this behavior to the docs: https://github.com/Kistler-Group/sdbus-cpp/blob/master/docs/using-sdbus-c++.md#standard-d-bus-interfaces

I think I should also add *_proxy classes for these standard interfaces. introspectable_proxy is there already, but others are missing. That would perhaps also help you writing clients to these interfaces...

@mdw87
Copy link
Author

mdw87 commented May 9, 2019

For some reason I am still having trouble getting the ObjectManager to show up. I put together a simple c++ application that creates a D-Bus object with a single interface and property. When I look at that object in D-Feet, it doesn't list the ObjectManager interface.

However, I was able to get it to show up by explicitly calling sd_bus_add_object_manager on the connection. I made a branch here showing the work to do that:
https://github.com/Kistler-Group/sdbus-cpp/compare/master...mdw87:feature/add-objectmanager-to-connection?expand=1

When I add this function call, I am able to see the ObjectManager in D-Feet, and calling the GetManagedObjects function works as expected. Do you know why I am not be seeing the ObjectManager added by default?

Here's the simple c++ program:

#include <sdbus-c++/sdbus-c++.h>

int main(int argc, char *argv[])
{
    // Create D-Bus connection to the system bus and requests name on it.
    const char* serviceName = "org.example.service";
    auto connection = sdbus::createSystemBusConnection(serviceName);


    // Create D-Bus object.
    const char* objectPath = "/org/example/service";
    auto serviceObject = sdbus::createObject(*connection, objectPath);

    // Register D-Bus methods and signals on the object, and export the object.
    const char* interfaceName = "org.example.service";
    serviceObject->registerProperty("Name").onInterface(interfaceName).withGetter([](){ return "hello"; }).withSetter([](const uint32_t& value){  });

    serviceObject->finishRegistration();

    // Start the service
    connection->enterProcessingLoop();
}

Here's the same program with the call to sd_bus_add_object_manager added. This will require the changes on my branch to compile.

#include <sdbus-c++/sdbus-c++.h>

int main(int argc, char *argv[])
{
    // Create D-Bus connection to the system bus and requests name on it.
    const char* serviceName = "org.example.service";
    auto connection = sdbus::createSystemBusConnection(serviceName);


    // Create D-Bus object.
    const char* objectPath = "/org/example/service";
    auto serviceObject = sdbus::createObject(*connection, objectPath);

    // Register D-Bus property on the object, and export the object.
    const char* interfaceName = "org.example.service";
    serviceObject->registerProperty("Name").onInterface(interfaceName).withGetter([](){ return "hello"; }).withSetter([](const uint32_t& value){  });

    serviceObject->finishRegistration();

    // Add an objectManager interface at the root of the service
    connection->addObjectManager("/");

    // Start the service
    connection->enterProcessingLoop();
}

The attached screenshots show how the service shows up in D-Feet before and after adding the function call.
without-objectmanager
with-object-manager

@sangelovic
Copy link
Collaborator

Having ObjectManager not as a default interface for every D-Bus object is a design decision of sd-bus, the underlying D-Bus implementation. It is available, just that it has to be added explicitly for a given object path. I've accepted your changes (thank you) and extended the IObject API as well. I've added a *_proxy classes for convenience. So sdbus-c++ now natively supports ObjectManager API as well as other D-Bus interfaces. However there's still lacking support for emitting Properties- and ObjectManager-related signals. This will follow in near future...

@wpd
Copy link

wpd commented Apr 26, 2021

Returning to this after a month away...

Hello @mdw87 - were you able to get sdbus-cpp, Bluez, and ObjectManager to play well with each other? Is your code public? Could I look at it for inspirational purposes?

Hello @sangelovic - did you add support for emitting Properties- and ObjectManager-related signals? How critical (do you think) support for those is?

--wpd

@sangelovic
Copy link
Collaborator

@wpd Yes, that was added just a few days later. See 01e2a7a and 38b51bd

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants