D-Bus tutorial in C to communicate with wpa_supplicant

morandg picture morandg · Dec 15, 2011 · Viewed 18.2k times · Source

I'm trying to write some code to communicate with wpa_supplicant using DBUS. As I'm working in an embedded system (ARM), I'd like to avoid the use of Python or the GLib. I'm wondering if I'm stupid because I really have the feeling that there is no nice and clear documentation about D-Bus. Even with the official one, I either find the documentation too high level, or the examples shown are using Glib! Documentation I've looked at: http://www.freedesktop.org/wiki/Software/dbus

I found a nice article about using D-Bus in C: http://www.matthew.ath.cx/articles/dbus

However, this article is pretty old and not complete enough! I also found the c++-dbus API but also here, I don't find ANY documentation! I've been digging into wpa_supplicant and NetworkManager source code but it's quite a nightmare! I've been looking into the "low-level D-Bus API" as well but this doesn't tell me how to extract a string parameter from a D-Bus message! http://dbus.freedesktop.org/doc/api/html/index.html

Here is some code I wrote to test a little but I really have trouble to extract string values. Sorry for the long source code but if someone want to try it ... My D-Bus configuration seems fine because it "already" catches "StateChanged" signals from wpa_supplicant but cannot print the state:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

#include <dbus/dbus.h>

//#include "wpa_supp_dbus.h"
/* Content of wpa_supp_dbus.h */
#define WPAS_DBUS_SERVICE   "fi.epitest.hostap.WPASupplicant"
#define WPAS_DBUS_PATH      "/fi/epitest/hostap/WPASupplicant"
#define WPAS_DBUS_INTERFACE "fi.epitest.hostap.WPASupplicant"

#define WPAS_DBUS_PATH_INTERFACES   WPAS_DBUS_PATH "/Interfaces"
#define WPAS_DBUS_IFACE_INTERFACE   WPAS_DBUS_INTERFACE ".Interface"

#define WPAS_DBUS_NETWORKS_PART "Networks"
#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network"

#define WPAS_DBUS_BSSIDS_PART   "BSSIDs"
#define WPAS_DBUS_IFACE_BSSID   WPAS_DBUS_INTERFACE ".BSSID"

int running = 1;

void stopLoop(int sig)
{
    running = 0;
}

void sendScan()
{
  // TODO !
}

void loop(DBusConnection* conn)
{
    DBusMessage* msg;
    DBusMessageIter args;
    DBusMessageIter subArgs;
    int argType;
    int i;
    int buffSize = 1024;
    char strValue[buffSize];
    const char* member = 0;

    sendScan();

    while (running)
    {
        // non blocking read of the next available message
        dbus_connection_read_write(conn, 0);
        msg = dbus_connection_pop_message(conn);

        // loop again if we haven't read a message
        if (!msg)
        {
            printf("No message received, waiting a little ...\n");
            sleep(1);
            continue;
        }
        else printf("Got a message, will analyze it ...\n");

        // Print the message member
        printf("Got message for interface %s\n",
                dbus_message_get_interface(msg));
        member = dbus_message_get_member(msg);
        if(member) printf("Got message member %s\n", member);

        // Check has argument
        if (!dbus_message_iter_init(msg, &args))
        {
            printf("Message has no argument\n");
            continue;
        }
        else
        {
            // Go through arguments
            while(1)
            {
                argType = dbus_message_iter_get_arg_type(&args);

                if (argType == DBUS_TYPE_STRING)
                {
                    printf("Got string argument, extracting ...\n");

                    /* FIXME : got weird characters
                    dbus_message_iter_get_basic(&args, &strValue);
                    */

                    /* FIXME : segmentation fault !
                    dbus_message_iter_get_fixed_array(
                            &args, &strValue, buffSize);
                    */

                    /* FIXME : segmentation fault !
                    dbus_message_iter_recurse(&args, &subArgs);
                    */

                    /* FIXME : deprecated!
                    if(dbus_message_iter_get_array_len(&args) > buffSize)
                        printf("message content to big for local buffer!");
                    */

                    //printf("String value was %s\n", strValue);
                }
                else
                    printf("Arg type not implemented yet !\n");

                if(dbus_message_iter_has_next(&args))
                    dbus_message_iter_next(&args);
                else break;
            }
            printf("No more arguments!\n");
        }

        // free the message
        dbus_message_unref(msg);
    }
}

int main(int argc, char* argv[])
{
    DBusError err;
    DBusConnection* conn;
    int ret;
    char signalDesc[1024];     // Signal description as string

    // Signal handling
    signal(SIGKILL, stopLoop);
    signal(SIGTERM, stopLoop);

    // Initialize err struct
    dbus_error_init(&err);

    // connect to the bus
    conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
    if (dbus_error_is_set(&err))
    {
        fprintf(stderr, "Connection Error (%s)\n", err.message);
        dbus_error_free(&err);
    }
    if (!conn)
    {
        exit(1);
    }

    // request a name on the bus
    ret = dbus_bus_request_name(conn, WPAS_DBUS_SERVICE, 0, &err);
    if (dbus_error_is_set(&err))
    {
        fprintf(stderr, "Name Error (%s)\n", err.message);
        dbus_error_free(&err);
    }

    /* Connect to signal */
    // Interface signal ..
    sprintf(signalDesc, "type='signal',interface='%s'",
            WPAS_DBUS_IFACE_INTERFACE);
    dbus_bus_add_match(conn, signalDesc, &err);
    dbus_connection_flush(conn);
    if (dbus_error_is_set(&err))
    {
        fprintf(stderr, "Match Error (%s)\n", err.message);
        exit(1);
    }

    // Network signal ..
    sprintf(signalDesc, "type='signal',interface='%s'",
            WPAS_DBUS_IFACE_NETWORK);
    dbus_bus_add_match(conn, signalDesc, &err);
    dbus_connection_flush(conn);
    if (dbus_error_is_set(&err))
    {
        fprintf(stderr, "Match Error (%s)\n", err.message);
        exit(1);
    }

    // Bssid signal ..
    sprintf(signalDesc, "type='signal',interface='%s'",
            WPAS_DBUS_IFACE_BSSID);
    dbus_bus_add_match(conn, signalDesc, &err);
    dbus_connection_flush(conn);
    if (dbus_error_is_set(&err))
    {
        fprintf(stderr, "Match Error (%s)\n", err.message);
        exit(1);
    }

    // Do main loop
    loop(conn);

    // Main loop exited
    printf("Main loop stopped, exiting ...\n");

    dbus_connection_close(conn);

    return 0;
}

Any pointer to any nice, complete, low-level C tutorial is strongly appreciated! I'm also planning to do some remote method call, so if the tutorial covers this subject it would be great! Saying I'm not very smart because I don't get it with the official tutorial is also appreciated :-p!

Or is there another way to communicate with wpa_supplicant (except using wpa_cli)?

EDIT 1:

Using 'qdbusviewer' and the introspection capabilty, this helped me a lot discovering what and how wpa_supplicant works using dbus. Hopping that this would help someone else!

Edit 2:

Will probably come when I'll find a way to read string values on D-Bus!

Answer

hdante picture hdante · Jul 16, 2013

You have given up the tools that would help you to learn D-Bus more easily and are using the low level libdbus implementation, so maybe you deserve to be in pain. BTW, are you talking about ARM, like a cell phone ARM ? With maybe 500 Mhz and 256 MB RAM ? In this case the processor is well suited to using glib, Qt or even python. And D-Bus is most useful when you're writing asynchronous event driven code, with an integrated main loop, for example from glib, even when you're using the low level libdbus (it has functions to connect to the glib main loop, for example).

Since you're using the low level library, then documentation is what you already have:

http://dbus.freedesktop.org/doc/api/html/index.html

Also, libdbus source code is also part of the documentation:

http://dbus.freedesktop.org/doc/api/html/files.html

The main entry point for the documentation is the Modules page (in particular, the public API section):

http://dbus.freedesktop.org/doc/api/html/modules.html

For message handling, the section DBusMessage is the relevant one: DBusMessage

There you have the documentation for functions that parse item values. In your case, you started with a dbus_message_iter_get_basic. As described in the docs, retrieving the string requires a const char ** variable, since the returned value will point to the pre-allocated string in the received message:

So for int32 it should be a "dbus_int32_t*" and for string a "const char**". The returned value is by reference and should not be freed.

So you can't define an array, because libdbus won't copy the text to your array. If you need to save the string, first get the constant string reference, then strcpy to your own array.

Then you tried to get a fixed array without moving the iterator. You need a call to the next iterator (dbus_message_iter_next) between the basic string and the fixed array. Same right before recursing into the sub iterator.

Finally, you don't call get_array_len to get the number of elements on the array. From the docs, it only returns byte counts. Instead you loop over the sub iterator using iter_next the same way you should have done with the main iterator. After you have iterated past the end of the array, dbus_message_iter_get_arg_type will return DBUS_TYPE_INVALID.

For more info, read the reference manual, don't look for a tutorial. Or just use a reasonable d-bus implementation:

https://developer.gnome.org/gio/2.36/gdbus-codegen.html

GIO's GDBus automatically creates wrappers for your d-bus calls.

http://qt-project.org/doc/qt-4.8/intro-to-dbus.html

http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html

etc.