clinuxbluetoothdbus

Having trouble toggling Bluetooth functionality using sd-bus in C


I'm currently working on a project where I need to control Bluetooth functionality using sd-bus. I'm relatively new to sd-bus and would appreciate some assistance.

Specifically, I'm trying to write C code to replicate the functionality of the following command:

busctl set-property org.bluez /org/bluez/hci0 org.bluez.Adapter1 Powered b true

and

busctl set-property org.bluez /org/bluez/hci0 org.bluez.Adapter1 Powered b false

using the sd-bus library. I've attempted to achieve this using the sd_bus_set_property() function, and later using the method call approach similar to the source code of "busctl set-property" available here.

Below are the snippets of the code I've tried:

Code Snippet 1: Using sd_bus_set_property() Function:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <systemd/sd-bus.h>

int main(int argc, char *argv[]) {
    sd_bus_error error = SD_BUS_ERROR_NULL;
    sd_bus *bus = NULL;
    int r;
    bool powered = true;  /* to turn on*/
    //bool powered = false;  /* to turn off*/

    /* Connect to the system bus */
    r = sd_bus_open_system(&bus);
    if (r < 0) {
        fprintf(stderr, "Failed to connect to system bus: %s\n", strerror(-r));
        goto finish;
    }
    printf("Connected to system bus\n");

    /* Set the property value */
    r = sd_bus_set_property(bus,
                            "org.bluez",        /* service to contact */
                            "/org/bluez/hci0",  /* object path */
                            "org.bluez.Adapter1",/* interface name */
                            "Powered",          /* property name */
                            &error,             /* object to return error in */
                            "b",                /* type */
                            &powered            /* pointer to boolean variable */
                           );
    if (r < 0) {
        fprintf(stderr, "Failed to set property: %s\n", error.message);
        goto finish;
    }
    printf("Successfully turned off\n");

finish:
    sd_bus_error_free(&error);
    sd_bus_unref(bus);

    return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}

Code Snippet 2: Using Method Call Approach:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <systemd/sd-bus.h>

int main(int argc, char *argv[]) {
    sd_bus *bus = NULL;
    sd_bus_message *m = NULL;
    sd_bus_error error = SD_BUS_ERROR_NULL; 
    int r;
    bool powered = true;  /* to turn on*/
    //bool powered = false;  /* to turn off*/
    
    r = sd_bus_open_system(&bus);
    if (r < 0) {
        fprintf(stderr, "Failed to connect to system bus: %s\n", strerror(-r));
        goto finish;
    }
    printf("Connected to system bus\n");
    
    r = sd_bus_message_new_method_call(bus, &m, "org.bluez", "/org/bluez/hci0",
                                       "org.freedesktop.DBus.Properties", "Set");
    if (r < 0) {
        fprintf(stderr, "Failed to create method call: %s\n", strerror(-r));
        goto finish;
    }
    
    r = sd_bus_message_append(m, "ss", "org.bluez.Adapter1", "Powered");
    if (r < 0) {
        fprintf(stderr, "Failed to append message: %s\n", strerror(-r));
        goto finish;
    }
    
    r = sd_bus_message_open_container(m, 'v', "b");
    if (r < 0) {
        fprintf(stderr, "Failed to open container: %s\n", strerror(-r));
        goto finish;
    }
    
    r = sd_bus_message_append_basic(m, 'b', &powered);
     if (r < 0) {
        fprintf(stderr, "Failed append_basic: %s\n", strerror(-r));
        goto finish;
    }
    
    r = sd_bus_message_close_container(m);
     if (r < 0) {
        fprintf(stderr, "Failed to close container: %s\n", strerror(-r));
        goto finish;
    }
    
    r = sd_bus_call(bus, m, 0, &error, NULL);
    if (r < 0) {
        fprintf(stderr, "Failed to make method call: %s\n", error.message);
        goto finish;
    }
    
finish:
    sd_bus_error_free(&error);
    sd_bus_message_unref(m);
    sd_bus_unref(bus);

    return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}

When I set the value of powered to true and run the code, the adapter powers up as expected. However, when I set powered to false, the Bluetooth doesn't turn off. Additionally, if the Bluetooth is already off, the code seems to power up the adapter even when powered is set to false.

I'd greatly appreciate any insights or assistance regarding this issue. Thank you in advance!


Solution

  • With anything that involves sending messages, always try looking at the actual messages: for IP networking, tcpdump; for D-Bus, use busctl monitor or dbus-monitor.

    For basic types, sd_bus_set_property() doesn't want pointers to the value – it wants the value directly, so your pointer-to-bool is always interpreted as "true" which you could see in the message being sent. Changing the &powered to powered should make the call work.

    See the manual for sd_bus_message_append(3), which has a table with "Expected C type" for each type specifier. It's more like printf() than ioctl().)

    (The second example with sd_bus_message_append_basic() does want a pointer, and it seems to generate an identical message to busctl set-property – of course, only after you uncomment the bool powered = false.)