Search code examples
cgdbuswpa-supplicant

Registering a signal handler with wpa_supplicant p2p DBus interface


I'm running into issues trying to register to receive the "InvitationReceived" signal from wpa_supplicant's dbus interface for p2p using the gdbus library in C.

I can create a proxy connection to the P2P dbus interface just fine and call methods on it, but when I try to connect a signal handler to the proxy, I just get the following error saying the signal is invalid (the relevant output from the code sample):

(process:6764): GLib-GObject-WARNING **: /tmp/buildd/glib2.0-2.42.1/./gobject/gsignal.c:2461: signal 'InvitationReceived' is invalid for instance '0x909ae0' of type 'GDBusProxy'

Which is weird, since "InvitationReceived" is the name of the signal as defined by the wpa_supplicant dbus api.


Code Sample:

static void on_wpa_ready (GObject *source_object,
                          GAsyncResult *res,
                          gpointer user_data) {
    g_print("on_wpa_ready\n");

    GError *error = NULL;
    GVariant *output;  

    GDBusProxy *p2p_proxy = g_dbus_proxy_new_for_bus_finish(res, &error);
    if (error) {
        g_print("proxy finish error: %s\n", error->message);
        g_error_free(error);
        return;
    }

    /* call p2p_listen */   
    g_clear_error(&error);
    output = NULL;
    output = g_dbus_proxy_call_sync(p2p_proxy,
                      "Listen",
                      g_variant_new("(i)", 0), //params
                      G_DBUS_CALL_FLAGS_NONE,
                      10, //timeout_msec
                      NULL,
                      &error
    );

    if (error) {
        g_print("Listen call error: %s\n", error->message);
        g_error_free(error);
        g_print("continuing...\n");
    }
    else {
        /* it gets to this print stmt, so the method was able to be called */
        g_print("Listen successful\n");
    }

    /* register for signal from p2p device */
    /* THIS IS WHERE THE ERROR IS */
    error = NULL;
    g_signal_connect(p2p_proxy,
                     "InvitationReceived",
                     G_CALLBACK(on_signal), // stub func that does something simple
                     NULL);

}

int main (int argc, char **argv) {
    GMainLoop *loop;

    /* connect to wpa_supplicant p2p dbus interface */
    g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
                     G_DBUS_PROXY_FLAGS_NONE,
                     NULL,
                     "fi.w1.wpa_supplicant1", //name,
                     "/fi/w1/wpa_supplicant1/Interfaces/0", //object_path,
                     "fi.w1.wpa_supplicant1.Interface.P2PDevice", //interface_name,
                     NULL,
                     on_wpa_ready, //callback,
                     NULL);
    );

    loop = g_main_loop_new(NULL, FALSE);
    g_main_loop_run(loop);
}

Is there some special path that needs to be appended to the signal name? Or am I supposed to use a different proxy for registering signal handlers from the one used to call methods?


Solution

  • (Finally found the problem / a solution -- posting here for future gdbus users)


    From the GIO documentation for GDBusProxy:

    The generic “g-properties-changed” and “g-signal” signals are not very convenient to work with. Therefore, the recommended way of working with proxies is to subclass GDBusProxy, and have more natural properties and signals in your derived class.


    As this paragraph hints, it turns out that the base GDBusProxy class only supports the two generic signals that all interfaces are guaranteed to have: g-properties-changed and g-signal. Registering for the g-signal signal on a proxy invokes the callback whenever any signal is emitted by the object of the given proxy, including the one I happened to be looking for in this case.

    Instead of going the subclassing route suggested in the documentation, I ended up just using a quick-and-dirty catchall handler:

    // the new snippet for connecting a signal handler:
    g_signal_connect(p2p_proxy,
                     "g-signal",
                     G_CALLBACK(on_signal), // stub func that does something simple
                     NULL);
    

    And then checking for which signal was actually called in the callback function:

    /* callback function */
    static void on_signal (GDBusProxy *proxy,
                           gchar *sender_name,
                           gchar *signal_name,
                           GVariant *params,
                           gpointer user_data) {
        if (g_strcmp0(signal_name, "InvitationReceived")) {
            // ignore all irrelevant signals
            return;
        } 
    
        g_print("InvitationReceived signal received\n");
    
        /* other handling code.... */
    }