Как я могу получить функцию g_dbus_connection_signal_subscribe, чтобы рассказать мне о ранее существующих объектах / интерфейсах?
Функция g_dbus_connection_signal_subscribe отлично работает, чтобы сообщить мне, когда новые объекты DBus появляются (или исчезают) с сигналом InterfacesAdded (или сигналом InterfacesRemoved). Но мне нужно знать о уже существующих объектах / интерфейсах.
Я написал следующий фрагмент кода на C для обеспечения обратных вызовов при добавлении / удалении объектов DBus из шины. Проверка ошибок для простоты опущена.
#include <stdio.h>
#include <stdlib.h>
#include <gio/gio.h>
static void signal_cb(GDBusConnection *connection,
const gchar *sender_name,const gchar *object_path,
const gchar *interface_name,const gchar *signal_name,
GVariant *parameters,gpointer user_data)
{
printf("%s: %s.%s %s\n",object_path,interface_name,signal_name,
g_variant_print(parameters,TRUE));
}
int main(int argc,char *argv[])
{
GDBusConnection *c;
GMainLoop *loop;
int filter_id;
c = g_bus_get_sync(G_BUS_TYPE_SYSTEM,NULL,&err);
loop = g_main_loop_new(NULL,0);
filter_id = g_dbus_connection_signal_subscribe(c,
"org.bluez",NULL,NULL,NULL,NULL,
G_DBUS_SIGNAL_FLAGS_NONE,signal_cb,NULL,NULL);
g_main_loop_run (loop);
g_main_loop_unref (loop);
exit(0);
}
Поэтому я пытаюсь отслеживать все объекты DBus, которые существуют в ветви дерева org.bluez. (Они представляют собой подключаемые контроллеры Bluetooth и устройства, обнаруженные каждым контроллером). Мне нужно знать об объектах DBus, которые уже были там до запуска моей программы, и мне нужно знать о новых объектах, которые появляются после запуска моей программы.
Мой код выше говорит мне о новых объектах, но ничего об объектах, которые уже есть. Есть ли способ в gdbus API для получения сигнала "InterfacesCreated" для объектов, которые уже существуют? Я полагаю, что можно прочитать всю иерархию объектов DBus, а затем подписаться на изменения, но это приводит к условиям гонки, когда, если объект появляется между временем, когда я читаю иерархию объектов, и временем, на которое я подписываюсь, я бы пропустил эти объекты....
Каков наилучший способ добиться этого с помощью API gdbus?
1 ответ
В случае, если кто-то случится с этим, как отметил Партибан, решение (для сервисов D-Bus, которые реализуют org.freedesktop.DBus.ObjectManager
интерфейс, такой как BlueZ), чтобы вызвать GetManagedObjects
метод. Кодирование на C с помощью GDBus крайне болезненно, поскольку требует детального понимания GVariant
типирование; см. документацию Также проверьте документы на GVariant
строки типа данных. Но вот как это делается:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <gio/gio.h>
/* The GVariant must be of type "a{sa{sv}}" (array of interfaces, where each */
/* interface has an array of properties). */
/* This type what DBus provides with InterfacesAdded signal and is also */
/* part of the return value from the GetManagedObjects method. */
static void proc_interface_var(const gchar *objpath,GVariant *iflist)
{
GVariantIter *iter,*iter2;
gchar *ifname,*propname,*proptext;
GVariant *propvalue;
g_variant_get(iflist,"a{sa{sv}}",&iter);
while (g_variant_iter_loop(iter,"{sa{sv}}",&ifname,&iter2)) {
if (strcmp(ifname,"org.bluez.Adatper1") != 0 &&
strcmp(ifname,"org.bluez.Device1") != 0) {
/* we only care about the Adatper1 and Device1 interfaces */
continue;
} /* if */
printf("Interface %s added to object %s\n",ifname,objpath);
while (g_variant_iter_loop(iter2,"{sv}",&propname,&propvalue)) {
proptext = g_variant_print(propvalue,0);
printf("\t%s=%s\n",propname,proptext);
g_free(proptext);
} /* while */
} /* while */
return;
} /* proc_interface_var */
/* The GVariant must be of type "a{sv}" (an array of properties). */
static void proc_property_var(const gchar *objpath,
gchar *ifname,GVariant *proplist)
{
GVariantIter *iter;
gchar *propname,*proptext;
GVariant *propvalue;
g_variant_get(proplist,"a{sv}",&iter);
while (g_variant_iter_loop(iter,"{sv}",&propname,&propvalue)) {
proptext = g_variant_print(propvalue,0);
printf("\tProperty changed on object %s interface %s: %s=%s\n",
objpath,ifname,propname,proptext);
g_free(proptext);
} /* while */
return;
} /* proc_property_var */
static void signal_cb(GDBusConnection *c,
const gchar *sender_name,const gchar *object_path,
const gchar *interface_name,const gchar *signal_name,
GVariant *parameters,gpointer user_data)
{
char fullsignal[200];
gchar *s,*objpath,*ifname,*propname,*proptext;
GVariant *ifvar,*propvalue;
GVariantIter *iter,*iter2;
snprintf(fullsignal,200,"%s.%s",interface_name,signal_name);
if (strcmp(fullsignal,
"org.freedesktop.DBus.ObjectManager.InterfacesAdded") == 0) {
g_variant_get(parameters,"(o*)",&objpath,&ifvar);
proc_interface_var(objpath,ifvar);
} else if (strcmp(fullsignal,
"org.freedesktop.DBus.Properties.PropertiesChanged") == 0) {
g_variant_get(parameters,"(s*as)",&ifname,&propvalue,&iter2);
proc_property_var(object_path,ifname,propvalue);
while (g_variant_iter_loop(iter2,"s",&propname)) {
printf("\tProperty changed on object %s interface %s: "
"%s is nil\n",object_path,ifname,propname);
} /* while */
} else {
printf("Ignoring unsupported signal for object %s, "
"signal=%s.%s, param type=%s\n",
object_path,interface_name,signal_name,
g_variant_get_type_string(parameters));
s = g_variant_print(parameters,TRUE);
printf("Unsupported signal: parameters %s\n",s);
g_free(s);
} /* else */
return;
} /* signal_cb */
static void bt_discover(GDBusConnection *c,const char *ctlname,int on_off)
{
GError *err=NULL;
GVariant *result;
const char *method;
char ctlpath[80];
snprintf(ctlpath,80,"/org/bluez/%s",ctlname);
method = on_off ? "StartDiscovery" : "StopDiscovery";
result = g_dbus_connection_call_sync(c,"org.bluez",ctlpath,
"org.bluez.Adapter1",method,NULL,
G_VARIANT_TYPE("()"), /* return-type */
G_DBUS_CALL_FLAGS_NONE,5000,NULL,&err);
if (result==NULL) {
if (err) fprintf(stderr,"g_dbus_connection_call error: %s\n",
err->message);
exit(1);
} /* if */
g_variant_unref(result);
return;
} /* bt_discover */
static void *receive_dbus_signals(void *arg)
{
GMainLoop *loop;
printf("Receiving DBus signals...\n");
loop = g_main_loop_new(NULL,0);
g_main_loop_run(loop);
g_main_loop_unref(loop);
return NULL;
} /* receive_dbus_signals */
int main(int argc,char *argv[])
{
GError *err=NULL;
GVariant *result,*ifvar;
GVariantIter *iter;
GDBusConnection *c;
GDBusNodeInfo *node;
gchar *objpath;
pthread_t handle;
int filter_id;
if ((c = g_bus_get_sync(G_BUS_TYPE_SYSTEM,NULL,&err)) == NULL) {
if (err) fprintf(stderr,"g_bus_get error: %s\n",err->message);
exit(1);
} /* if */
filter_id = g_dbus_connection_signal_subscribe(c,
"org.bluez",NULL,NULL,NULL,NULL,
G_DBUS_SIGNAL_FLAGS_NONE,signal_cb,NULL,NULL);
if (pthread_create(&handle,NULL,receive_dbus_signals,NULL) != 0) {
fprintf(stderr,"Failed to create DBus listen thread\n");
exit(1);
} /* if */
result = g_dbus_connection_call_sync(c,"org.bluez","/",
"org.freedesktop.DBus.ObjectManager","GetManagedObjects",NULL,
G_VARIANT_TYPE("(a{oa{sa{sv}}})"), /* return-type */
G_DBUS_CALL_FLAGS_NONE,5000,NULL,&err);
if (result==NULL) {
if (err) fprintf(stderr,"g_dbus_connection_call error: %s\n",
err->message);
exit(1);
} /* if */
g_variant_get(result,"(a{oa{sa{sv}}})",&iter);
/* below we replace 'a{sa{sv}}' with '*' to get it as a GVariant */
while (g_variant_iter_loop(iter,"{o*}",&objpath,&ifvar)) {
proc_interface_var(objpath,ifvar);
} /* while */
g_variant_unref(result);
bt_discover(c,"hci0",1);
sleep(5);
bt_discover(c,"hci0",0);
sleep(5);
exit(0);
}
Для услуг D-Bus, которые не реализуют org.freedesktop.DBus.ObjectManager
В интерфейсе вам необходимо использовать самоанализ D-Bus и анализировать XML самоанализ, чтобы найти пути к существующим объектным узлам.