cdbusbusctl

New C sdbus interface code seems to work but also gets org.freedesktop.DBus.Error.UnknownMethod


I'm trying to use the sdbus for the first time, with mixed success. On the one hand, the code seems to work, but it is also generating an error in the journal, which I don't know how to fix.

I'm running in Ubuntu 22.04 64-bit on VMWare. Here is my C code, which uses the sdbus:

#include <systemd/sd-bus.h>
#include <iostream>
#include <cstring>

// Callback for the method call
static int request_handler(sd_bus_message* msg, [[maybe_unused]] void* userdata, [[maybe_unused]] sd_bus_error* ret_error) {
    int64_t input;
    int ret;

    // Read the input string from the message
    ret = sd_bus_message_read(msg, "x", &input);
    if (ret < 0) {
        std::cerr << "Failed to read message: " << strerror(-ret) << std::endl;
        return ret;
    }

    std::cout << "Received message: " << input << std::endl;

    // Respond with a message
    int64_t response = 202;
    ret = sd_bus_reply_method_return(msg, "x", response);
    if (ret < 0) {
        std::cerr << "Failed to send reply: " << strerror(-ret) << std::endl;
        return ret;
    }

    return 0;
}

int main() {
    sd_bus* bus = nullptr;
    sd_bus_slot* slot = nullptr;
    int ret;

    // Connect to the system bus
    ret = sd_bus_open_system(&bus);
    if (ret < 0) {
        std::cerr << "Failed to connect to system bus: " << strerror(-ret) << std::endl;
        return 1;
    }

    // Register the object path and method
    ret = sd_bus_add_object_vtable(bus,
                                   &slot,
                                   "/com/WMServiceFunction/Object",          // Object path
                                   "com.WMServiceFunction.Interface",        // Interface
                                   (const sd_bus_vtable[]){
                                       SD_BUS_VTABLE_START(0),
                                       SD_BUS_METHOD("sendRequest", "x", "x", request_handler, SD_BUS_VTABLE_UNPRIVILEGED),
                                       SD_BUS_VTABLE_END},
                                   nullptr);
    if (ret < 0) {
        std::cerr << "Failed to register object: " << strerror(-ret) << std::endl;
        sd_bus_unref(bus);
        return 1;
    }

    // Request a well-known name on the bus
    ret = sd_bus_request_name(bus, "com.WMServiceFunction", 0);
    if (ret < 0) {
        std::cerr << "Failed to request name: " << strerror(-ret) << std::endl;
        sd_bus_unref(bus);
        return 1;
    }

    std::cout << "powerControld Service is running..." << std::endl;

    // Process requests
    while (true) {
        ret = sd_bus_process(bus, nullptr);
        if (ret < 0) {
            std::cerr << "Failed to process bus: " << strerror(-ret) << std::endl;
            break;
        }

        // Wait for the next event
        if (ret > 0) {
            continue;
        }

        ret = sd_bus_wait(bus, (uint64_t)-1);
        if (ret < 0) {
            std::cerr << "Failed to wait on bus: " << strerror(-ret) << std::endl;
            break;
        }
    }

    sd_bus_slot_unref(slot);
    sd_bus_unref(bus);

    return 0;
}

I compile my code with this command:

# g++ -g -o powercontrold-server powercontrold-server.c `pkg-config --cflags --libs libsystemd`

I understand that I need to add a configuration file for the sdbus, and know I can use a session or system interface. In my case, I'm using the system bus, so I created com.WMServiceFunction.conf in /etc/dbus-1/system.d, with the following contents:

<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
    <policy user="root">
        <allow own="com.WMServiceFunction"/>
        <allow send_destination="com.WMServiceFunction"/>
        <allow send_interface="com.WMServiceFunction.Interface"/>
        <allow receive_sender="com.WMServiceFunction"/>
    </policy>
</busconfig>

If I start my executable, powercontrold-server, running at a prompt, I'm able to see that the sdbus recognizes my new interface, with the well-known name com.WMServiceFunction, with this command: text

I can also see that my method, sendRequest, which I defined in the VTABLE is recognized, using busctl introspection as follows: text

So far, so good. Now, I want to call my method, sendRequest, which simply takes an integer, and returns another integer, as defined in the VTABLE, with sd_bus_add_object_vtable(). My request_handler() code gets invoked, and responds to the request, when I type in this command: text

I can see that my powercontrold-server receives the integer, 101, that I sent to it using the busctl command, and I can see the response, 202, comes back. If I had just quit at this point, I would have been a happy camper, but I had to go look at the system log, using journalctl, and saw this error: text

At this point, I started reading up on how to debug errors on the sdbus, and came across the "busctl monitor" command. When I entered this command, I got even more confused. I could see my method, sendRequest, was called, and could even see the integers that I sent and received, but then there were additional calls that left me perplexed. The output was very verbose, but here is a snippet from the output, which I think captures things: text

You can see the initial "Type=method_call", which shows "sendRequest" getting called, and sending the integer 101, and then the response "Type=method_return", which shows the integer 202 being returned. Here is where things go South. Why is there another entry in the log of "Type=error", with "ErrorName=org.freedesktop.DBus.Error.UnknownMethod", and the ErrorMessage="Unknown method sendRequest or interface com.WMServiceFunction.Interface." sendRequest is my own method, and it was just successfully handled before this error came out. Something on the DBUS obviously doesn't like the stuff I just defined, even though, it all seemed to work. Can anybody explain what I have done wrong, or what I am missing?


Solution

  • I just got back to chasing this further and found my own answer.

    I found a piece of code out on the web, which used the sdbus to send a message, which I was able to get working successfully. I then started working backwards to figure out what the difference was between my code and theirs and was able to narrow it down to the request_handler() function that was called when the sendRequest entry in my VTABLE was hit. In the request_handler() function, I had some local variables that were uninitialized. I found that when I initialized these local variables to zero, when they were declared, that the error messages I was seeing in the log went away.

    enter image description here

    I'm not exactly sure what was going on, under the hood, that led to the error messages in the journal output, but after initializing these variables, the errors are gone. It looks like the uninitialized variables led to some undefined behavior, that manifested itself in a very unexpected and cryptic fashion.

    It is probably worth mentioning, that I found a tool called d-spy, which was very useful in the debugging process. It is a tool intended to help explore d-bus connections, and can be used to execute and invoke methods on an interface, on both the session or system bus. It can be found here: https://flathub.org/apps/org.gnome.dspy

    Here is how I would test the set up of my interface, and invoke methods:

    enter image description here