coreos/rpm-ostree

dbus Subscribe to Signals for /run/rpm-ostree-transaction.sock Fails

Closed this issue · 1 comments

COM8 commented

Host system details

● ppos:fedora/38/x86_64/photon-pony-base
                  Version: PhotonPonyOSBase 38.20230830.0 (2023-08-30T08:20:51Z)
               BaseCommit: 64481d03e28be7bf5694b171b314c0f9e05f5ae177ad71bdbc54b0ddc469855a
             GPGSignature: Valid signature by F4AADD82266C8BC2DB3FC9B0D6763692AE005CBF
          LayeredPackages: 'g++' abseil-cpp-devel bustle clang clang-tools-extra cmake cppcheck d-feet d-spy dbus-devel dbus-tools dotnet-runtime-7.0 dotnet-sdk-7.0 doxygen gcovr gdb grpc-devel grpc-plugins libasan liblsan libtsan
                           libubsan libudev-devel nuget opalkelly plantuml python3-pip qt soci-devel spcm4 spcm4-akmod
            LocalPackages: cert-gen-1.5.0-1.noarch
                 Unlocked: development

  ppos:fedora/38/x86_64/photon-pony-base
                  Version: PhotonPonyOSBase 38.20230830.0 (2023-08-30T08:20:51Z)
               BaseCommit: 64481d03e28be7bf5694b171b314c0f9e05f5ae177ad71bdbc54b0ddc469855a
             GPGSignature: Valid signature by F4AADD82266C8BC2DB3FC9B0D6763692AE005CBF
          LayeredPackages: 'g++' abseil-cpp-devel bustle clang clang-tools-extra cmake cppcheck d-feet d-spy dbus-devel dotnet-runtime-7.0 dotnet-sdk-7.0 doxygen gcovr gdb grpc-devel grpc-plugins libasan liblsan libtsan libubsan
                           libudev-devel nuget opalkelly plantuml python3-pip soci-devel spcm4 spcm4-akmod
            LocalPackages: cert-gen-1.5.0-1.noarch

  ppos:fedora/38/x86_64/photon-pony-base
                  Version: PhotonPonyOSBase 38.20230828.0 (2023-08-28T08:39:50Z)
               BaseCommit: 6c242a5ce9a03eb48868008888f7dd03fe6c524955659c2572c0ad16f244b7dc
             GPGSignature: Valid signature by F4AADD82266C8BC2DB3FC9B0D6763692AE005CBF
          LayeredPackages: 'g++' abseil-cpp-devel clang clang-tools-extra cmake cppcheck d-spy dbus-devel dotnet-runtime-7.0 dotnet-sdk-7.0 doxygen gcovr gdb grpc-devel grpc-plugins libasan liblsan libtsan libubsan libudev-devel nuget
                           opalkelly plantuml python3-pip soci-devel spcm4 spcm4-akmod
                   Pinned: yes

  ppos:fedora/38/x86_64/photon-pony-base
                  Version: PhotonPonyOSBase 38.20230828.0 (2023-08-28T08:39:50Z)
               BaseCommit: 6c242a5ce9a03eb48868008888f7dd03fe6c524955659c2572c0ad16f244b7dc
             GPGSignature: Valid signature by F4AADD82266C8BC2DB3FC9B0D6763692AE005CBF
          LayeredPackages: 'g++' abseil-cpp-devel clang clang-tools-extra cmake cppcheck d-spy dbus-devel dotnet-runtime-7.0 dotnet-sdk-7.0 doxygen gcovr gdb grpc-devel grpc-plugins libasan liblsan libtsan libubsan libudev-devel nuget
                           plantuml python3-pip soci-devel
                   Pinned: yes

Expected vs actual behavior
I can successfully trigger an Upgrade org.projectatomic.rpmostree1.Upgrade. Then I successfully connect to the returned transaction_address using libdbus. There I can call org.projectatomic.rpmostree1.Transaction.Start or even org.projectatomic.rpmostree1.Transaction.Cancel. BUT I'm failing to subscribe to signals. I always get: NAME - org.freedesktop.DBus.Error.UnknownMethod, MESSAGE - Object does not exist at path “/org/freedesktop/DBus”'.

I tried to reduce my code to the following example showing the error. Invoking it after one has started an upgrade via for example d-spy results in Match Error (Object does not exist at path “/org/freedesktop/DBus”).

Expected:

Subscribing to events should work as expected.

Steps to reproduce it

all:
    	gcc test.c -o test $(shell pkg-config --cflags --libs dbus-1)

Code:

#include <dbus/dbus.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    DBusError err;
    dbus_error_init(&err);

    // Connect to the custom D-Bus socket
    DBusConnection* conn = dbus_connection_open("unix:path=/run/rpm-ostree-transaction.sock", &err);
    if (dbus_error_is_set(&err)) {
        fprintf(stderr, "Connection Error: %s\n", err.message);
        dbus_error_free(&err);
    }
    if (conn == NULL) {
        exit(1);
    }
    printf("Connected\n");

    dbus_error_init(&err);
    dbus_bus_add_match(conn, "type='signal', sender='org.projectatomic.rpmostree1', interface='org.projectatomic.rpmostree1.Transaction', member='Message', path='/'", &err);
    if (dbus_error_is_set(&err)) {
        fprintf(stderr, "Match Error (%s)\n", err.message);
        dbus_error_free(&err);
        exit(1);
    }
    printf("Registered!");

    // Main loop
    while (dbus_connection_read_write_dispatch(conn, -1)) {
        // your custom handling code
    }

    // Clean up
    dbus_connection_unref(conn);

    return 0;
}
rpm-ostree install dbus-devel
make
./test
💣

Would you like to work on the issue?

I tried to, but I'm failing to find any hints. I even tried to take a look how cockpit-ostree handles it here: https://github.com/cockpit-project/cockpit-ostree/blob/bdb2e19f6e42c696f94656f385689089278d5ba7/src/client.js#L653-L676

But as far as I can see it, they are doing the same thing.

Solution

Since the daemon offers only an implementation without /org/freedesktop/DBus we can not use dbus_bus_add_match(...) for delegated filtering. A solution is to listen to all incoming events and then filter manually.

#include <dbus/dbus.h>
#include <stdio.h>
#include <stdlib.h>

DBusHandlerResult signal_filter (DBusConnection *connection, DBusMessage *message, void *user_data) {
    if (dbus_message_is_signal(message, "org.projectatomic.rpmostree1.Transaction", "Finished")) {
        printf("FINISHED Signal\n");
    } else if (dbus_message_is_signal(message, "org.projectatomic.rpmostree1.Transaction", "Message")) {
        printf("MESSAGE Signal\n");
    }

    // Print the message type
    int type = dbus_message_get_type(message);
    const char *type_str = dbus_message_type_to_string(type);
    printf("Message Type: %s\n", type_str);

    // Print the interface, if present
    const char *interface = dbus_message_get_interface(message);
    if (interface) {
        printf("Interface: %s\n", interface);
    }

    // Print the method or signal name, if present
    const char *member = dbus_message_get_member(message);
    if (member) {
        printf("Member: %s\n", member);
    }

    // Print the sender, if present
    const char *sender = dbus_message_get_sender(message);
    if (sender) {
        printf("Sender: %s\n", sender);
    }

    // Print the destination, if present
    const char *destination = dbus_message_get_destination(message);
    if (destination) {
        printf("Destination: %s\n", destination);
    }

    // Handle specific message types if necessary
    if (type == DBUS_MESSAGE_TYPE_SIGNAL) {
        // Handle signals
    } else if (type == DBUS_MESSAGE_TYPE_METHOD_CALL) {
        // Handle method calls
    }

    printf("\n");  // Add a new line for clarity
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

int main() {
    DBusError err;
    dbus_error_init(&err);

    // Connect to the custom D-Bus socket
    DBusConnection* conn = dbus_connection_open("unix:path=/run/rpm-ostree-transaction.sock", &err);
    if (dbus_error_is_set(&err)) {
        fprintf(stderr, "Connection Error: %s\n", err.message);
        dbus_error_free(&err);
        exit(1);
    }

    if (conn == NULL) {
        exit(1);
    }
    printf("Connected\n");

    dbus_connection_add_filter(conn, signal_filter, NULL, NULL);
    printf("Registered!\n");

    // Main loop
    while (dbus_connection_read_write_dispatch(conn, -1)) {
        // your custom handling code
    }

    // Clean up
    dbus_connection_remove_filter(conn, signal_filter, NULL);
    dbus_connection_unref(conn);

    return 0;
}