[libvirt] [dbus PATCH v3 1/5] introduce support for GDBus implementation

Pavel Hrdina phrdina at redhat.com
Wed Mar 21 10:02:43 UTC 2018


We will switch to GDBus implementation of D-Bus protocol because
sd-bus implementation is not thread safe.

Processing messages in threads is essential since Libvirt API can
take some significant amount of time to return and that would block
the whole libvirt-dbus daemon.

Signed-off-by: Pavel Hrdina <phrdina at redhat.com>
---

Changes in v3:
  - fixed a bug while loading XML interface files, now it fails
    gracefully with error message

Changes in v2:
  - changed glib2 required version to 2.44.0, required for the
    auto-cleanup macros
  - changed libvirt-glib required version to 0.0.7

 README               |   1 +
 configure.ac         |  12 ++
 data/Makefile.am     |   5 +
 libvirt-dbus.spec.in |   6 +
 src/Makefile.am      |  17 ++-
 src/gdbus.c          | 398 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/gdbus.h          | 108 ++++++++++++++
 test/Makefile.am     |   3 +-
 test/travis-run      |   2 +-
 9 files changed, 547 insertions(+), 5 deletions(-)
 create mode 100644 src/gdbus.c
 create mode 100644 src/gdbus.h

diff --git a/README b/README
index 754d957..a85114e 100644
--- a/README
+++ b/README
@@ -58,6 +58,7 @@ The packages required to build libvirt-dbus are
 
  - systemd-211
  - libvirt
+ - glib2
 
 Patches submissions
 ===================
diff --git a/configure.ac b/configure.ac
index df1a375..0e3bc01 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11,10 +11,14 @@ AC_USE_SYSTEM_EXTENSIONS
 
 AM_SILENT_RULES([yes])
 
+GLIB2_REQUIRED=2.44.0
 LIBVIRT_REQUIRED=1.2.8
 SYSTEMD_REQUIRED=211
+LIBVIRT_GLIB_REQUIRED=0.0.7
+AC_SUBST([GLIB2_REQUIRED]) dnl used in the .spec file
 AC_SUBST([LIBVIRT_REQUIRED]) dnl used in the .spec file
 AC_SUBST([SYSTEMD_REQUIRED]) dnl used in the .spec file
+AC_SUBST([LIBVIRT_GLIB_REQUIRED]) dnl used in the .spec file
 
 LIBVIRT_DBUS_MAJOR_VERSION=`echo $VERSION | awk -F. '{print $1}'`
 LIBVIRT_DBUS_MINOR_VERSION=`echo $VERSION | awk -F. '{print $2}'`
@@ -34,8 +38,11 @@ AC_PROG_MKDIR_P
 AM_PROG_CC_C_O
 AC_PROG_CC_STDC
 
+PKG_CHECK_MODULES(GIO2, gio-unix-2.0 >= GLIB2_REQUIRED)
+PKG_CHECK_MODULES(GLIB2, glib-2.0 >= GLIB2_REQUIRED)
 PKG_CHECK_MODULES(LIBVIRT, libvirt >= $LIBVIRT_REQUIRED)
 PKG_CHECK_MODULES(SYSTEMD, libsystemd >= $SYSTEMD_REQUIRED)
+PKG_CHECK_MODULES(LIBVIRT_GLIB, libvirt-glib-1.0 >= LIBVIRT_GLIB_REQUIRED)
 
 LIBVIRT_COMPILE_WARNINGS
 LIBVIRT_LINKER_RELRO
@@ -56,6 +63,11 @@ LIBVIRT_ARG_WITH([DBUS_SYSTEM_POLICIES], [where D-Bus system policies directory
 DBUS_SYSTEM_POLICIES_DIR="$with_dbus_system_policies"
 AC_SUBST(DBUS_SYSTEM_POLICIES_DIR)
 
+LIBVIRT_ARG_WITH([DBUS_INTERFACES], [where D-Bus interfaces directory is],
+                 ['$(datadir)/dbus-1/interfaces'])
+DBUS_INTERFACES_DIR="$with_dbus_interfaces"
+AC_SUBST([DBUS_INTERFACES_DIR])
+
 LIBVIRT_ARG_WITH([SYSTEM_USER], [username to run system instance as],
                  ['libvirtdbus'])
 SYSTEM_USER=$with_system_user
diff --git a/data/Makefile.am b/data/Makefile.am
index 9a53305..a886687 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -18,11 +18,16 @@ polkit_files = \
 polkitdir = $(sysconfdir)/polkit-1/rules.d
 polkit_DATA = $(polkit_files:.rules.in=.rules)
 
+interfaces_files =
+interfacesdir = $(DBUS_INTERFACES_DIR)
+interfaces_DATA = $(interfaces_files)
+
 EXTRA_DIST = \
 	$(service_in_files) \
 	$(system_service_in_files) \
 	$(system_policy_files) \
 	$(polkit_files) \
+	$(interfaces_files) \
 	$(NULL)
 
 CLEANFILES = \
diff --git a/libvirt-dbus.spec.in b/libvirt-dbus.spec.in
index ce5cecf..662ece1 100644
--- a/libvirt-dbus.spec.in
+++ b/libvirt-dbus.spec.in
@@ -1,7 +1,9 @@
 # -*- rpm-spec -*-
 
+%define glib2_version @GLIB2_REQUIRED@
 %define libvirt_version @LIBVIRT_REQUIRED@
 %define systemd_version @SYSTEMD_REQUIRED@
+%define libvirt_glib_version @LIBVIRT_GLIB_REQUIRED@
 %define system_user @SYSTEM_USER@
 
 Name: @PACKAGE@
@@ -15,11 +17,15 @@ Source0: ftp://libvirt.org/libvirt/dbus/%{name}-%{version}.tar.gz
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
 BuildRequires: gcc
+BuildRequires: glib2-devel >= %{glib2_version}
 BuildRequires: libvirt-devel >= %{libvirt_version}
 BuildRequires: systemd-devel >= %{systemd_version}
+BuildRequires: libvirt-glib-devel >= %{libvirt_glib_version}
 
+Requires: glib2 >= %{glib2_version}
 Requires: libvirt-libs >= %{libvirt_version}
 Requires: systemd-libs >= %{systemd_version}
+Requires: libvirt-glib >= %{libvirt_glib_version}
 
 Requires(pre): shadow-utils
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 1a5b50b..e7bba9d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,12 +1,14 @@
 AM_CPPFLAGS = \
-	-I$(top_srcdir)/src
+	-I$(top_srcdir)/src \
+	-DVIRT_DBUS_INTERFACES_DIR=\"$(DBUS_INTERFACES_DIR)\"
 
 DAEMON_SOURCES = \
 	main.c \
 	connect.c connect.h \
 	util.c util.h \
 	domain.c domain.h \
-	events.c events.h
+	events.c events.h \
+	gdbus.c gdbus.h
 
 EXTRA_DIST = \
 	$(DAEMON_SOURCES)
@@ -18,18 +20,27 @@ libvirt_dbus_SOURCES = $(DAEMON_SOURCES)
 
 libvirt_dbus_CFLAGS = \
 	$(SYSTEMD_CFLAGS) \
+	$(GIO2_CFLAGS) \
+	$(GLIB2_CFLAGS) \
 	$(LIBVIRT_CFLAGS) \
+	$(LIBVIRT_GLIB_CFLAGS) \
 	$(WARN_CFLAGS) \
 	$(PIE_CFLAGS) \
 	$(NULL)
 
 libvirt_dbus_LDFLAGS = \
 	$(SYSTEMD_LDFLAGS) \
+	$(GIO2_LDFLAGS) \
+	$(GLIB2_LDFLAGS) \
 	$(LIBVIRT_LDFLAGS) \
+	$(LIBVIRT_GLIB_LDFLAGS) \
 	$(RELRO_LDFLAGS) \
 	$(PID_LDFLAGS) \
 	$(NULL)
 
 libvirt_dbus_LDADD = \
 	$(SYSTEMD_LIBS) \
-	$(LIBVIRT_LIBS)
+	$(GIO2_LIBS) \
+	$(GLIB2_LIBS) \
+	$(LIBVIRT_LIBS) \
+	$(LIBVIRT_GLIB_LIBS)
diff --git a/src/gdbus.c b/src/gdbus.c
new file mode 100644
index 0000000..cbfd07d
--- /dev/null
+++ b/src/gdbus.c
@@ -0,0 +1,398 @@
+#include "gdbus.h"
+
+#include <glib/gprintf.h>
+
+struct _virtDBusGDBusMethodData {
+    virtDBusGDBusMethodTable *methods;
+    virtDBusGDBusPropertyTable *properties;
+    gpointer *userData;
+};
+typedef struct _virtDBusGDBusMethodData virtDBusGDBusMethodData;
+
+struct _virtDBusGDBusSubtreeData {
+    GDBusInterfaceInfo *interface;
+    virtDBusGDBusEnumerateFunc enumerate;
+    virtDBusGDBusMethodData *methodData;
+};
+typedef struct _virtDBusGDBusSubtreeData virtDBusGDBusSubtreeData;
+
+static const gchar *dbusInterfacePrefix = NULL;
+
+/**
+ * virtDBusGDBusLoadIntrospectData:
+ * @interface: name of the interface
+ * @error: return location for error
+ *
+ * Reads an interface XML description from file and returns new
+ * interface info.  The caller owns an reference to the returned info.
+ *
+ * The file path is constructed as:
+ *
+ *  VIRT_DBUS_INTERFACES_DIR/{@interface}.xml
+ *
+ * Returns interface info on success, NULL on failure.
+ */
+GDBusInterfaceInfo *
+virtDBusGDBusLoadIntrospectData(gchar const *interface,
+                                GError **error)
+{
+    g_autofree gchar *introspectFile = NULL;
+    g_autofree gchar *introspectXML = NULL;
+    g_autoptr(GDBusNodeInfo) nodeInfo = NULL;
+    GDBusInterfaceInfo *ret;
+
+    if (!dbusInterfacePrefix) {
+        dbusInterfacePrefix = g_getenv("VIRT_DBUS_INTERFACES_DIR");
+        if (!dbusInterfacePrefix)
+            dbusInterfacePrefix = VIRT_DBUS_INTERFACES_DIR;
+    }
+
+    introspectFile = g_strdup_printf("%s/%s.xml", dbusInterfacePrefix, interface);
+
+    if (!g_file_get_contents(introspectFile, &introspectXML, NULL, error))
+        return NULL;
+
+    nodeInfo = g_dbus_node_info_new_for_xml(introspectXML, error);
+    if (!nodeInfo)
+        return NULL;
+
+    ret = nodeInfo->interfaces[0];
+    if (!ret) {
+        g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+                    "no interface defined in '%s'", introspectFile);
+        return NULL;
+    }
+
+    return g_dbus_interface_info_ref(ret);
+}
+
+static void
+virtDBusGDBusHandlePropertyGet(GVariant *parameters,
+                               GDBusMethodInvocation *invocation,
+                               const gchar *objectPath,
+                               virtDBusGDBusMethodData *data)
+{
+    virtDBusGDBusPropertyGetFunc getFunc = NULL;
+    const gchar *interface;
+    const gchar *name;
+    GVariant *value = NULL;
+    GError *error = NULL;
+
+    g_variant_get(parameters, "(&s&s)", &interface, &name);
+
+    for (gint i = 0; data->properties[i].name; i++) {
+        if (g_strcmp0(name, data->properties[i].name) == 0) {
+            getFunc = data->properties[i].getFunc;
+            break;
+        }
+    }
+
+    if (!getFunc) {
+        g_dbus_method_invocation_return_error(invocation,
+                                              G_DBUS_ERROR,
+                                              G_DBUS_ERROR_UNKNOWN_PROPERTY,
+                                              "unknown property '%s'", name);
+        return;
+    }
+
+    getFunc(objectPath, data->userData, &value, &error);
+
+    if (error) {
+        g_dbus_method_invocation_return_gerror(invocation, error);
+        return;
+    }
+
+    g_return_if_fail(value);
+
+    g_dbus_method_invocation_return_value(invocation,
+                                          g_variant_new("(v)", value));
+}
+
+static void
+virtDBusGDBusHandlePropertySet(GVariant *parameters,
+                               GDBusMethodInvocation *invocation,
+                               const gchar *objectPath,
+                               virtDBusGDBusMethodData *data)
+{
+    virtDBusGDBusPropertySetFunc setFunc = NULL;
+    const gchar *interface;
+    const gchar *name;
+    g_autoptr(GVariant) value = NULL;
+    GError *error = NULL;
+
+    g_variant_get(parameters, "(&s&sv)", &interface, &name, &value);
+
+    for (gint i = 0; data->properties[i].name; i++) {
+        if (g_strcmp0(name, data->properties[i].name) == 0) {
+            setFunc = data->properties[i].setFunc;
+            break;
+        }
+    }
+
+    if (!setFunc) {
+        g_dbus_method_invocation_return_error(invocation,
+                                              G_DBUS_ERROR,
+                                              G_DBUS_ERROR_UNKNOWN_PROPERTY,
+                                              "unknown property '%s'", name);
+        return;
+    }
+
+    setFunc(value, objectPath, data->userData, &error);
+
+    if (error)
+        g_dbus_method_invocation_return_gerror(invocation, error);
+    else
+        g_dbus_method_invocation_return_value(invocation, NULL);
+}
+
+static void
+virtDBusGDBusHandlePropertyGetAll(GDBusMethodInvocation *invocation,
+                                  const gchar *objectPath,
+                                  virtDBusGDBusMethodData *data)
+{
+    GVariant *value;
+    g_auto(GVariantBuilder) builder;
+    GError *error = NULL;
+
+    g_variant_builder_init(&builder, G_VARIANT_TYPE("(a{sv})"));
+
+    g_variant_builder_open(&builder, G_VARIANT_TYPE("a{sv}"));
+
+    for (gint i = 0; data->properties[i].name; i++) {
+        data->properties[i].getFunc(objectPath, data->userData,
+                                    &value, &error);
+
+        if (error) {
+            g_dbus_method_invocation_return_gerror(invocation, error);
+            return;
+        }
+
+        g_return_if_fail(value);
+
+        g_variant_builder_add(&builder, "{sv}",
+                              data->properties[i].name,
+                              g_variant_new_variant(value));
+    }
+
+    g_variant_builder_close(&builder);
+
+    g_dbus_method_invocation_return_value(invocation,
+                                          g_variant_builder_end(&builder));
+}
+
+static void
+virtDBusGDBusHandleMethod(GVariant *parameters,
+                          GDBusMethodInvocation *invocation,
+                          const gchar *objectPath,
+                          const gchar *methodName,
+                          virtDBusGDBusMethodData *data)
+{
+    virtDBusGDBusMethodFunc methodFunc = NULL;
+    GDBusMessage *msg = g_dbus_method_invocation_get_message(invocation);
+    GUnixFDList *inFDs = NULL;
+    GVariant *outArgs = NULL;
+    GUnixFDList *outFDs = NULL;
+    GError *error = NULL;
+
+    for (gint i = 0; data->methods[i].name; i++) {
+        if (g_strcmp0(methodName, data->methods[i].name) == 0) {
+            methodFunc = data->methods[i].methodFunc;
+            break;
+        }
+    }
+
+    if (!methodFunc) {
+        g_dbus_method_invocation_return_error(invocation,
+                                              G_DBUS_ERROR,
+                                              G_DBUS_ERROR_UNKNOWN_METHOD,
+                                              "unknown method '%s'", methodName);
+        return;
+    }
+
+    inFDs = g_dbus_message_get_unix_fd_list(msg);
+
+    methodFunc(parameters, inFDs, objectPath, data->userData,
+               &outArgs, &outFDs, &error);
+
+    if (error) {
+        g_dbus_method_invocation_return_gerror(invocation, error);
+        return;
+    }
+
+    g_return_if_fail(outArgs || !outFDs);
+
+    g_dbus_method_invocation_return_value_with_unix_fd_list(invocation,
+                                                            outArgs,
+                                                            outFDs);
+}
+
+static void
+virtDBusGDBusHandleMethodCall(GDBusConnection *connection G_GNUC_UNUSED,
+                              const gchar *sender G_GNUC_UNUSED,
+                              const gchar *objectPath,
+                              const gchar *interfaceName,
+                              const gchar *methodName,
+                              GVariant *parameters,
+                              GDBusMethodInvocation *invocation,
+                              gpointer userData)
+{
+    virtDBusGDBusMethodData *data = userData;
+
+    if (g_strcmp0(interfaceName, "org.freedesktop.DBus.Properties") == 0) {
+        if (g_strcmp0(methodName, "Get") == 0) {
+            virtDBusGDBusHandlePropertyGet(parameters, invocation,
+                                           objectPath, data);
+        } else if (g_strcmp0(methodName, "Set") == 0) {
+            virtDBusGDBusHandlePropertySet(parameters, invocation,
+                                           objectPath, data);
+        } else if (g_strcmp0(methodName, "GetAll") == 0) {
+            virtDBusGDBusHandlePropertyGetAll(invocation, objectPath, data);
+        } else {
+            g_dbus_method_invocation_return_error(invocation,
+                                                  G_DBUS_ERROR,
+                                                  G_DBUS_ERROR_UNKNOWN_METHOD,
+                                                  "unknown method '%s'", methodName);
+        }
+    } else {
+        virtDBusGDBusHandleMethod(parameters, invocation, objectPath,
+                                  methodName, data);
+    }
+}
+
+static const GDBusInterfaceVTable virtDBusGDBusVtable = {
+    virtDBusGDBusHandleMethodCall,
+    NULL,
+    NULL,
+    { NULL }
+};
+
+/**
+ * virtDBusGDBusRegisterObject:
+ * @bus: GDBus connection
+ * @objectPath: object path
+ * @interface: interface info of the object
+ * @methods: table of method handlers
+ * @properties: table of property handlers
+ * @userData: data that are passed to method and property handlers
+ *
+ * Registers a new D-Bus object that we would like to handle.
+ */
+void
+virtDBusGDBusRegisterObject(GDBusConnection *bus,
+                            gchar const *objectPath,
+                            GDBusInterfaceInfo *interface,
+                            virtDBusGDBusMethodTable *methods,
+                            virtDBusGDBusPropertyTable *properties,
+                            gpointer userData)
+{
+    virtDBusGDBusMethodData *data = g_new0(virtDBusGDBusMethodData, 1);
+
+    data->methods = methods;
+    data->properties = properties;
+    data->userData = userData;
+
+    g_dbus_connection_register_object(bus,
+                                      objectPath,
+                                      interface,
+                                      &virtDBusGDBusVtable,
+                                      data, g_free,
+                                      NULL);
+}
+
+static gchar **
+virtDBusGDBusEnumerate(GDBusConnection *connection G_GNUC_UNUSED,
+                       const gchar *sender G_GNUC_UNUSED,
+                       const gchar *objectPath G_GNUC_UNUSED,
+                       gpointer userData)
+{
+    virtDBusGDBusSubtreeData *data = userData;
+
+    if (data->enumerate)
+        return data->enumerate(data->methodData->userData);
+
+    return NULL;
+}
+
+static GDBusInterfaceInfo **
+virtDBusGDBusIntrospect(GDBusConnection *bus G_GNUC_UNUSED,
+                        const gchar *sender G_GNUC_UNUSED,
+                        const gchar *objectPath G_GNUC_UNUSED,
+                        const gchar *node G_GNUC_UNUSED,
+                        gpointer userData)
+{
+    virtDBusGDBusSubtreeData *data = userData;
+    GDBusInterfaceInfo **ret = g_new0(GDBusInterfaceInfo *, 2);
+
+    ret[0] = g_dbus_interface_info_ref(data->interface);
+
+    return ret;
+}
+
+static const GDBusInterfaceVTable *
+virtDBusGDBusDispatch(GDBusConnection *bus G_GNUC_UNUSED,
+                      const gchar *sender G_GNUC_UNUSED,
+                      const gchar *objectPath G_GNUC_UNUSED,
+                      const gchar *interfaceName G_GNUC_UNUSED,
+                      const gchar *node G_GNUC_UNUSED,
+                      gpointer *outUserData,
+                      gpointer userData)
+{
+    virtDBusGDBusSubtreeData *data = userData;
+
+    *outUserData = data->methodData;
+    return &virtDBusGDBusVtable;
+}
+
+static const GDBusSubtreeVTable virtDBusGDBusSubreeVtable = {
+    virtDBusGDBusEnumerate,
+    virtDBusGDBusIntrospect,
+    virtDBusGDBusDispatch,
+    { NULL }
+};
+
+static void
+virtDBusGDBusSubtreeDataFree(gpointer opaque)
+{
+    virtDBusGDBusSubtreeData *data = opaque;
+    g_free(data->methodData);
+    g_free(data);
+}
+
+/**
+ * virtDBusGDBusRegisterSubtree:
+ * @bus: GDBus connection
+ * @objectPath: object prefix path
+ * @interface: interface info of the object
+ * @methods: table of method handlers
+ * @properties: table of property handlers
+ * @userData: data that are passed to method and property handlers
+ *
+ * Registers a new D-Bus object prefix that we would like to handle.
+ */
+void
+virtDBusGDBusRegisterSubtree(GDBusConnection *bus,
+                             gchar const *objectPath,
+                             GDBusInterfaceInfo *interface,
+                             virtDBusGDBusEnumerateFunc enumerate,
+                             virtDBusGDBusMethodTable *methods,
+                             virtDBusGDBusPropertyTable *properties,
+                             gpointer userData)
+{
+    virtDBusGDBusSubtreeData *data = g_new0(virtDBusGDBusSubtreeData, 1);
+
+    data->methodData = g_new0(virtDBusGDBusMethodData, 1);
+
+    data->interface = interface;
+    data->enumerate = enumerate;
+    data->methodData->methods = methods;
+    data->methodData->properties = properties;
+    data->methodData->userData = userData;
+
+    g_dbus_connection_register_subtree(bus,
+                                       objectPath,
+                                       &virtDBusGDBusSubreeVtable,
+                                       G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES,
+                                       data,
+                                       virtDBusGDBusSubtreeDataFree,
+                                       NULL);
+}
diff --git a/src/gdbus.h b/src/gdbus.h
new file mode 100644
index 0000000..6c1a8a3
--- /dev/null
+++ b/src/gdbus.h
@@ -0,0 +1,108 @@
+#pragma once
+
+#include <gio/gio.h>
+
+/**
+ * virtDBusGDBusMethodFunc:
+ * @inArgs: input arguments of the method call
+ * @inFDs: list of input file descriptors
+ * @objectPath: the object path the method was called on
+ * @userData: user data passed when registering new object or subtree
+ * @outArgs: return location of output arguments
+ * @outFDs: return location of output file descriptors
+ * @error: return location for error
+ *
+ * Handles D-Bus method call.  In case of error the handler has
+ * to set an @error.
+ */
+typedef void
+(*virtDBusGDBusMethodFunc)(GVariant *inArgs,
+                           GUnixFDList *inFDs,
+                           const gchar *objectPath,
+                           gpointer userData,
+                           GVariant **outArgs,
+                           GUnixFDList **outFDs,
+                           GError **error);
+
+/**
+ * virtDBusGDBusPropertyGetFunc:
+ * @objectPath: the object path the method was called on
+ * @userData: user data passed when registering new object or subtree
+ * @value: return location for property value
+ * @error: return location for error
+ *
+ * Handles D-Bus Get action on a property.  In case of error the handler
+ * has to set an @error, otherwise @value has to be set.
+ */
+typedef void
+(*virtDBusGDBusPropertyGetFunc)(const gchar *objectPath,
+                                gpointer userData,
+                                GVariant **value,
+                                GError **error);
+
+/**
+ * virtDBusGDBusPropertySetFunc:
+ * @objectPath: the object path the method was called on
+ * @value: new value that should be set to the property
+ * @userData: user data passed when registering new object or subtree
+ * @error: return location for error
+ *
+ * Handles D-Bus Set action on a property.  In case of error the handler
+ * has to set an @error.
+ */
+typedef void
+(*virtDBusGDBusPropertySetFunc)(GVariant *value,
+                                const gchar *objectPath,
+                                gpointer userData,
+                                GError **error);
+
+/**
+ * virtDBusGDBusEnumerateFunc:
+ * @userData: user data passed when registering new subtree
+ *
+ * Handles D-Bus introspection for subtree of objects.
+ *
+ * Returns a list of objects or NULL.
+ */
+typedef gchar **
+(*virtDBusGDBusEnumerateFunc)(gpointer userData);
+
+struct _virtDBusGDBusMethodTable {
+    const gchar *name;
+    virtDBusGDBusMethodFunc methodFunc;
+};
+typedef struct _virtDBusGDBusMethodTable virtDBusGDBusMethodTable;
+
+struct _virtDBusGDBusPropertyTable {
+    const gchar *name;
+    virtDBusGDBusPropertyGetFunc getFunc;
+    virtDBusGDBusPropertySetFunc setFunc;
+};
+typedef struct _virtDBusGDBusPropertyTable virtDBusGDBusPropertyTable;
+
+typedef guint virtDBusGDBusSource;
+typedef guint virtDBusGDBusOwner;
+
+GDBusInterfaceInfo *
+virtDBusGDBusLoadIntrospectData(gchar const *interface,
+                                GError **error);
+
+void
+virtDBusGDBusRegisterObject(GDBusConnection *bus,
+                            gchar const *objectPath,
+                            GDBusInterfaceInfo *interface,
+                            virtDBusGDBusMethodTable *methods,
+                            virtDBusGDBusPropertyTable *properties,
+                            gpointer userData);
+
+void
+virtDBusGDBusRegisterSubtree(GDBusConnection *bus,
+                             gchar const *objectPath,
+                             GDBusInterfaceInfo *interface,
+                             virtDBusGDBusEnumerateFunc enumerate,
+                             virtDBusGDBusMethodTable *methods,
+                             virtDBusGDBusPropertyTable *properties,
+                             gpointer userData);
+
+G_DEFINE_AUTO_CLEANUP_FREE_FUNC(virtDBusGDBusSource, g_source_remove, 0);
+G_DEFINE_AUTO_CLEANUP_FREE_FUNC(virtDBusGDBusOwner, g_bus_unown_name, 0);
diff --git a/test/Makefile.am b/test/Makefile.am
index 4651d08..d3997f3 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -13,4 +13,5 @@ EXTRA_DIST = \
 	travis-run
 
 TESTS_ENVIRONMENT = \
-	abs_top_builddir=$(abs_top_builddir)
+	abs_top_builddir=$(abs_top_builddir) \
+	VIRT_DBUS_INTERFACES_DIR=$(abs_top_srcdir)/data
diff --git a/test/travis-run b/test/travis-run
index 482b286..28260f4 100755
--- a/test/travis-run
+++ b/test/travis-run
@@ -22,7 +22,7 @@ sudo chroot "$CHROOT" << EOF
 set -ex
 # install build deps
 apt-get update
-apt-get install -y dh-autoreconf pkg-config libvirt-dev libsystemd-dev libtool python3-gi python3-dbus dbus
+apt-get install -y dh-autoreconf pkg-config libvirt-dev libsystemd-dev libglib2.0-dev libtool python3-gi python3-dbus dbus
 
 # run build and tests as user
 chown -R buildd:buildd /build
-- 
2.14.3




More information about the libvir-list mailing list