我正在尝试编写一些代码来使用DBUS与wpa_supplicant进行通信。当我在嵌入式系统(ARM)中工作时,我想避免使用Python或GLib。我想知道我是不是很愚蠢,因为我真的觉得没有关于D-Bus的清晰文档。即使使用官方文档,我发现文档的级别太高,或者显示的示例都使用了Glib!我看过的文档:http://www.freedesktop.org/wiki/Software/dbus
我发现了一篇关于在C中使用D-Bus的好文章:http://www.matthew.ath.cx/articles/dbus
然而,这篇文章已经很老了,还不够完整!我也找到了c ++ - dbus API,但在这里,我找不到任何文档!我一直在深入研究wpa_supplicant和NetworkManager源代码,但这真是一场噩梦!我一直在研究“低级D-Bus API”,但这并没有告诉我如何从D-Bus消息中提取字符串参数! http://dbus.freedesktop.org/doc/api/html/index.html
这是我编写的一些代码,但是我真的很难提取字符串值。很抱歉有很长的源代码,但如果有人想尝试...我的D-Bus配置看起来很好,因为它“已经”从wpa_supplicant捕获“StateChanged”信号,但无法打印状态:
#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上读取字符串值的方法时,可能会来!
答案 0 :(得分:4)
您放弃了可以帮助您更轻松地学习D-Bus并使用低级别libdbus实现的工具,所以也许您应该感到痛苦。顺便说一句,你是在谈论ARM,就像手机ARM?可能有500 Mhz和256 MB RAM?在这种情况下,处理器非常适合使用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
文档的主要入口点是Modules页面(特别是公共API部分):
http://dbus.freedesktop.org/doc/api/html/modules.html
对于消息处理,DBusMessage部分是相关的: DBusMessage
您有解析项目值的函数的文档。在您的情况下,您开始使用dbus_message_iter_get_basic。如文档中所述,检索字符串需要const char **变量,因为返回的值将指向收到的消息中预先分配的字符串:
因此对于int32,它应该是“dbus_int32_t *”,而对于字符串,它应该是“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
等
答案 1 :(得分: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
答案 2 :(得分:0)
以下代码段适用于我
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);
}
答案 3 :(得分:0)
我怀疑该答案是否仍与该问题的作者有关, 但对于像我一样偶然发现此问题的人:
如果您不想将GTK / QT包含在访问dbus的项目中,现在的情况比几年前要好。 Embedded Linux Library by Intel中有dbus API(很奇怪,我记得它是开放的,也许现在仅适用于注册用户?) 现在,systemd sd-bus库提供了公共API。除非您有一个真正受限制的嵌入式系统,否则您可能仍要运行systemd。
我曾经使用过GDbus,dbus-cpp和sd-bus,尽管我想要一个C ++库, 我发现SD总线是最简单,问题最少的体验。 我没有尝试its C++ bindings but they also look nice
#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),但是由于我的所有加密狗都拒绝与我合作,我无法对其进行测试桌面。不过应该可以给您一个大致的想法。
请注意,您可能会遇到几个魔术或半魔术问题。 为确保顺利进行开发/测试,请执行以下操作:
ip link
中的无线接口已打开此外,因为目前没有足够的文档记录:
您可以通过sd_bus_message_enter_container
访问数组和内部变量值
和_exit对应。 sd_bus_message_peek_type
可能会派上用场。
或sd_bus_message_read_array
表示同质数组。