Подтвердить что ты не робот

Учебник D-Bus в C для общения с wpa_supplicant

Я пытаюсь написать код для связи с wpa_supplicant с помощью DBUS. Поскольку я работаю во встроенной системе (ARM), я бы хотел избежать использования Python или GLib. Мне интересно, я глуп, потому что у меня действительно есть ощущение, что нет никакой хорошей и четкой документации о D-Bus. Даже с официальным, я либо нахожу документацию слишком высокого уровня, либо примеры, показанные с помощью Glib! Документация, на которую я смотрел: http://www.freedesktop.org/wiki/Software/dbus

Я нашел хорошую статью об использовании D-Bus в C: http://www.matthew.ath.cx/articles/dbus

Однако эта статья довольно старая и недостаточно полная! Я также нашел API С++ - dbus, но и здесь, я не нашел ЛЮБОЙ документации! Я копал в исходный код wpa_supplicant и NetworkManager, но это довольно кошмар! Я также изучал "низкоуровневый D-Bus API", но это не говорит мне, как извлечь строковый параметр из сообщения D-Bus! http://dbus.freedesktop.org/doc/api/html/index.html

Вот некоторый код, который я написал, чтобы немного протестировать, но мне действительно трудно извлечь строковые значения. Извините за длинный исходный код, но если кто-то хочет попробовать его... Моя конфигурация D-Bus кажется прекрасной, потому что она уже "ловит" сигналы StateChanged от wpa_supplicant, но не может распечатать состояние:

#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;
}

Приветствуется любой указатель на любой хороший, полный, низкоуровневый учебник C! Я также планирую сделать вызов удаленного метода, поэтому, если учебник охватывает эту тему, было бы здорово! Говорить, что я не очень умный, потому что я не понимаю его с официальным руководством, также приветствуется: -p!

Или существует ли другой способ связи с wpa_supplicant (кроме использования wpa_cli)?

РЕДАКТИРОВАТЬ 1:

Используя "qdbusviewer" и возможности интроспекции, это помогло мне узнать, что и как wpa_supplicant работает с использованием dbus. Хоппинг, что это поможет кому-то еще!

Изменить 2:

Вероятно, придет, когда я найду способ чтения строковых значений на D-Bus!

4b9b3361

Ответ 1

Вы отказались от инструментов, которые помогут вам более легко изучить D-Bus и использовать низкоуровневую реализацию libdbus, поэтому, возможно, вы заслуживаете боли. Кстати, вы говорите об ARM, как о мобильном телефоне ARM? Может быть, 500 МГц и 256 МБ ОЗУ? В этом случае процессор хорошо подходит для использования glib, Qt или даже python. И D-Bus наиболее полезен, когда вы пишете асинхронный код, управляемый событиями, с интегрированным основным циклом, например, из glib, даже если вы используете низкоуровневый libdbus (у него есть функции для подключения к основному циклу glib, например).

Поскольку вы используете библиотеку низкого уровня, документация - это то, что у вас уже есть:

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

Кроме того, исходный код libdbus также является частью документации:

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

Основной точкой входа для документации является страница "Модули" (в частности, раздел "Открытый API" ):

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

Для обработки сообщений раздел DBusMessage является релевантным: DBusMessage

Там у вас есть документация для функций, которые анализируют значения элементов. В вашем случае вы начали с dbus_message_iter_get_basic. Как описано в документах, для извлечения строки требуется переменная const char **, так как возвращаемое значение будет указывать на предварительно выделенную строку в полученном сообщении:

Итак, для int32 он должен быть "dbus_int32_t *" и для строки a "const char **". Возвращаемое значение является ссылкой и не должно быть освобождено.

Таким образом, вы не можете определить массив, потому что libdbus не будет копировать текст в ваш массив. Если вам нужно сохранить строку, сначала получите константную ссылку на строку, затем strcpy в свой собственный массив.

Затем вы попытались получить фиксированный массив без перемещения итератора. Вам нужен вызов следующего итератора (dbus_message_iter_next) между основной строкой и фиксированным массивом. То же самое прямо перед повторением в суб-итератор.

Наконец, вы не вызываете get_array_len, чтобы получить количество элементов в массиве. Из документов он возвращает только количество байтов. Вместо этого вы перебираете суб-итератор с помощью iter_next так же, как и с основным итератором. После того, как вы закончили конец массива, dbus_message_iter_get_arg_type вернет DBUS_TYPE_INVALID.

Для получения дополнительной информации прочитайте справочное руководство, не смотрите учебник. Или просто используйте разумную реализацию d-bus:

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

GIO GDBus автоматически создает обертки для вызовов вашей d-bus.

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

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

и др.

Ответ 2

Вам не нужно использовать/понимать работу dbus. Если вам просто нужно написать программу на C для общения с wpa_supplicant. Я переделал исходный код wpa_cli. Прошел через его реализацию и использовал функции, предусмотренные в wpa_ctrl.h/c. Эта реализация заботится обо всем. Вы можете использовать/изменять все, что хотите, создать свой исполняемый файл, и все готово!

Здесь официальная ссылка на wpa_supplicant ctrl_interface: http://hostap.epitest.fi/wpa_supplicant/devel/ctrl_iface_page.html

Ответ 4

Ниже снимок работает для меня

if (argType == DBUS_TYPE_STRING)
{
printf("Got string argument, extracting ...\n");
char* strBuffer = NULL;
dbus_message_iter_get_basic(&args, &strBuffer);
printf("Received string: \n %s \n",strBuffer);

} 

Ответ 5

Я сомневаюсь, что этот ответ будет по-прежнему актуален для автора этого вопроса, но для любого, кто сталкивается с этим, как я:

Сейчас ситуация лучше, чем все эти годы назад, если вы не хотите включать GTK/QT в свой проект для доступа к dbus. В Embedded Linux Library от Intel есть API-интерфейс dbus (странно, я помню, что он открыт, может быть, он предназначен только для зарегистрированных пользователей?), А библиотека systemd sd-bus теперь предлагает публичный API. Вы, вероятно, в любом случае запустите systemd, если у вас нет действительно ограниченной встроенной системы.

Я работал с GDbus, dbus-cpp и sd-bus, и хотя мне нужна была библиотека C++, я обнаружил, что sd-bus - самый простой и наименее проблемный опыт. Я не пробовал привязки C++, но они также выглядят хорошо

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

const char* wpa_service = "fi.w1.wpa_supplicant1";
const char* wpa_root_obj_path = "/fi/w1/wpa_supplicant1";
const char* wpa_root_iface = "fi.w1.wpa_supplicant1";

sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus* system_bus = NULL;
sd_event* loop = NULL;
sd_bus_message* reply = NULL;

void cleanup() {
    sd_event_unref(loop);
    sd_bus_unref(system_bus);
    sd_bus_message_unref(reply);
    sd_bus_error_free(&error);
}

void print_error(const char* msg, int code) {
    fprintf(stderr, "%s %s\n", msg, strerror(-code));
    exit(EXIT_FAILURE);
}

const char* get_interface(const char* iface) {
    int res = sd_bus_call_method(system_bus,
                               wpa_service,
                               wpa_root_obj_path,
                               wpa_root_iface,
                               "GetInterface",
                               &error,
                               &reply,
                               "s",
                               "Ifname", "s", iface,
                               "Driver", "s", "nl80211");
    if (res < 0) {
        fprintf(stderr, "(get) error response: %s\n", error.message);
        return NULL;
    }

    const char* iface_path;
    /*
     * an object path was returned in reply
     * this works like an iterator, if a method returns (osu), you could call message_read_basic in succession
     * with arguments SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_STRING, SD_BUS_TYPE_UINT32 or you could
     * call sd_bus_message_read() and provides the signature + arguments in one call
     * */
    res = sd_bus_message_read_basic(reply, SD_BUS_TYPE_OBJECT_PATH, &iface_path);
    if (res < 0) {
        print_error("getIface: ", res);
        return NULL;
    }
    return iface_path;
}

const char* create_interface(const char* iface) {
    int res = sd_bus_call_method(system_bus,
                               wpa_service,
                               wpa_root_obj_path,
                               wpa_root_iface,
                               "CreateInterface",
                               &error,
                               &reply,
                               "a{sv}", 2, //pass array of str:variant (dbus dictionary) with 2
                                            //entries to CreateInterface
                               "Ifname", "s", iface, // "s" variant parameter contains string, then pass the value
                               "Driver", "s", "nl80211");
    if (res < 0) {
        fprintf(stderr, "(create) error response: %s\n", error.message);
        return NULL;
    }
    const char* iface_path;
    res = sd_bus_message_read_basic(reply, SD_BUS_TYPE_OBJECT_PATH, &iface_path);
    if (res < 0) {
        print_error("createIface: ", res);
    }
    return iface_path;
}

int main() {
    int res;
    const char* iface_path;

    //open connection to system bus - default either opens or reuses existing connection as necessary
    res = sd_bus_default_system(&system_bus);
    if (res < 0) {
        print_error("open: ", res);
    }

    //associate connection with event loop, again default either creates or reuses existing
    res = sd_event_default(&loop);
    if (res < 0) {
        print_error("event: ", res);
    }

    // get obj. path to the wireless interface on dbus so you can call methods on it
    // this is a wireless interface (e.g. your wifi dongle) NOT the dbus interface
    // if you don't know the interface name in advance, you will have to read the Interfaces property of
    // wpa_supplicants root interface — call Get method on org.freedesktop.DBus properties interface,
    // while some libraries expose some kind of get_property convenience function sd-bus does not
    const char* ifaceName = "wlp32s0f3u2";
    if (!(iface_path = get_interface(ifaceName))) { //substitute your wireless iface here
        // sometimes the HW is present and listed in "ip l" but dbus does not reflect that, this fixes it
        if (!(iface_path = create_interface(ifaceName))) {
            fprintf(stderr, "can't create iface: %s" , ifaceName);
            cleanup();
            return EXIT_FAILURE;
        }
    }

    /*
    call methods with obj. path iface_path and dbus interface of your choice
    this will likely be "fi.w1.wpa_supplicant1.Interface", register for signals etc...
    you will need the following to receive those signals
    */
    int runForUsec = 1000000; //usec, not msec!
    sd_event_run(loop, runForUsec); //or sd_event_loop(loop) if you want to loop forever

    cleanup();
    printf("Finished OK\n");
    return 0;
}

Я прошу прощения, если приведенный выше пример не работает идеально. Это отрывок из старого проекта, который я переписал для C из C++ (я думаю, что C (-ish), компилятор не протестует, а вы спросили C), но я не могу проверить его, так как все мои ключи отказываются работать с моим рабочим столом прямо сейчас. Это должно дать вам общее представление, хотя.

Обратите внимание, что вы, вероятно, столкнетесь с несколькими магическими или полумагическими проблемами. Для обеспечения бесперебойной разработки/тестирования сделайте следующее:

  1. убедитесь, что другие приложения управления сетью отключены (networkmanager, connman...)
  2. перезапустите сервис wpa_supplicant
  3. убедитесь, что в ip link беспроводной интерфейс

Кроме того, потому что это не очень хорошо документировано прямо сейчас: Вы можете получить доступ к массивам и внутренним значениям вариантов с помощью sd_bus_message_enter_container и _exit counterpart. sd_bus_message_peek_type может пригодиться при этом. Или sd_bus_message_read_array для однородного массива.