[libvirt] [dbus PATCH 16/18] introduce support for libdbus library

Pavel Hrdina posted 18 patches 7 years, 2 months ago
[libvirt] [dbus PATCH 16/18] introduce support for libdbus library
Posted by Pavel Hrdina 7 years, 2 months ago
We will switch to libdbus library because the systemd 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@redhat.com>
---
 README               |    1 +
 configure.ac         |   13 +
 data/Makefile.am     |    5 +
 libvirt-dbus.spec.in |    3 +
 src/Makefile.am      |   11 +-
 src/dbus.c           | 1226 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/dbus.h           |  158 +++++++
 src/domain.c         |    8 -
 src/util.c           |  131 +++++-
 src/util.h           |   11 +
 test/Makefile.am     |    3 +-
 test/travis-run      |    2 +-
 12 files changed, 1554 insertions(+), 18 deletions(-)
 create mode 100644 src/dbus.c
 create mode 100644 src/dbus.h

diff --git a/README b/README
index 754d957..242c9ba 100644
--- a/README
+++ b/README
@@ -57,6 +57,7 @@ raised in future releases based on this distro build target policy.
 The packages required to build libvirt-dbus are
 
  - systemd-211
+ - dbus
  - libvirt
 
 Patches submissions
diff --git a/configure.ac b/configure.ac
index aef3d37..c8dcf04 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,8 +13,10 @@ AM_SILENT_RULES([yes])
 
 LIBVIRT_REQUIRED=1.2.8
 SYSTEMD_REQUIRED=211
+DBUS_REQUIRED=1.10.24
 AC_SUBST([LIBVIRT_REQUIRED]) dnl used in the .spec file
 AC_SUBST([SYSTEMD_REQUIRED]) dnl used in the .spec file
+AC_SUBST([DBUS_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}'`
@@ -35,6 +37,7 @@ AM_PROG_CC_C_O
 
 PKG_CHECK_MODULES(LIBVIRT, libvirt >= $LIBVIRT_REQUIRED)
 PKG_CHECK_MODULES(SYSTEMD, libsystemd >= $SYSTEMD_REQUIRED)
+PKG_CHECK_MODULES(DBUS, dbus-1 >= $DBUS_REQUIRED)
 
 LIBVIRT_COMPILE_WARNINGS
 LIBVIRT_LINKER_RELRO
@@ -70,6 +73,16 @@ else
 fi
 AC_SUBST(DBUS_SYSTEM_POLICIES_DIR)
 
+AC_ARG_WITH(dbus-interfaces,
+            [AC_HELP_STRING([--with-dbus-interfaces=<dir>],
+            [where D-BUS interfaces directory is])])
+if ! test -z "$with_dbus_interfaces" ; then
+    DBUS_INTERFACES_DIR="with_dbus_interfaces"
+else
+    DBUS_INTERFACES_DIR="$datadir/dbus-1/interfaces"
+fi
+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 87f20d4..512f4fc 100644
--- a/libvirt-dbus.spec.in
+++ b/libvirt-dbus.spec.in
@@ -2,6 +2,7 @@
 
 %define libvirt_version @LIBVIRT_REQUIRED@
 %define systemd_version @SYSTEMD_REQUIRED@
+%define dbus_version @DBUS_REQUIRED@
 %define system_user @SYSTEM_USER@
 
 Name: @PACKAGE@
@@ -16,9 +17,11 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
 BuildRequires: libvirt-devel >= %{libvirt_version}
 BuildRequires: systemd-devel >= %{systemd_version}
+BuildRequires: dbus-devel >= %{dbus_version}
 
 Requires: libvirt-libs >= %{libvirt_version}
 Requires: systemd-libs >= %{systemd_version}
+Requires: dbus-libs >= %{dbus_version}
 
 Requires(pre): shadow-utils
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 1a5b50b..1f0d990 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,12 +1,16 @@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src
 
+AM_CPPFLAGS += \
+	-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 \
+	dbus.c dbus.h
 
 EXTRA_DIST = \
 	$(DAEMON_SOURCES)
@@ -19,6 +23,7 @@ libvirt_dbus_SOURCES = $(DAEMON_SOURCES)
 libvirt_dbus_CFLAGS = \
 	$(SYSTEMD_CFLAGS) \
 	$(LIBVIRT_CFLAGS) \
+	$(DBUS_CFLAGS) \
 	$(WARN_CFLAGS) \
 	$(PIE_CFLAGS) \
 	$(NULL)
@@ -26,10 +31,12 @@ libvirt_dbus_CFLAGS = \
 libvirt_dbus_LDFLAGS = \
 	$(SYSTEMD_LDFLAGS) \
 	$(LIBVIRT_LDFLAGS) \
+	$(DBUS_LDFLAGS) \
 	$(RELRO_LDFLAGS) \
 	$(PID_LDFLAGS) \
 	$(NULL)
 
 libvirt_dbus_LDADD = \
 	$(SYSTEMD_LIBS) \
-	$(LIBVIRT_LIBS)
+	$(LIBVIRT_LIBS) \
+	$(DBUS_LIBS)
diff --git a/src/dbus.c b/src/dbus.c
new file mode 100644
index 0000000..327ae12
--- /dev/null
+++ b/src/dbus.c
@@ -0,0 +1,1226 @@
+#define _GNU_SOURCE
+
+#include "dbus.h"
+#include "util.h"
+
+#include <libvirt/virterror.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct _virtDBusMessage {
+    DBusMessage *message;
+    DBusConnection *bus;
+    DBusError *error;
+    DBusMessageIter **stack;
+    size_t items;
+    size_t size;
+};
+
+struct _virtDBusObject {
+    char *path;
+    const char **introspectXML;
+    const char *interface;
+    virtDBusObjectType type;
+    virtDBusPropertyHandlers *properties;
+    virtDBusMethodHandlers *methods;
+    void *data;
+};
+
+static int
+virtDBusGetLibvirtEvents(DBusWatch *watch)
+{
+    unsigned int events;
+    int virt_events = 0;
+
+    events = dbus_watch_get_flags(watch);
+
+    if (events & DBUS_WATCH_READABLE)
+        virt_events |= VIR_EVENT_HANDLE_READABLE;
+
+    if (events & DBUS_WATCH_WRITABLE)
+        virt_events |= VIR_EVENT_HANDLE_WRITABLE;
+
+    return virt_events;
+}
+
+static unsigned int
+virtDBusGetBusEvents(int virt_events)
+{
+    unsigned int events = 0;
+
+    if (virt_events & VIR_EVENT_HANDLE_READABLE)
+        events |= DBUS_WATCH_READABLE;
+
+    if (virt_events & VIR_EVENT_HANDLE_WRITABLE)
+        events |= DBUS_WATCH_WRITABLE;
+
+    return events;
+}
+
+static void
+virtDBusHandleBusEvent(int watch VIRT_ATTR_UNUSED,
+                       int fd VIRT_ATTR_UNUSED,
+                       int events,
+                       void *opaque)
+{
+    dbus_watch_handle(opaque, virtDBusGetBusEvents(events));
+}
+
+static void
+virtDBusHandleBusTimeout(int timer VIRT_ATTR_UNUSED,
+                         void *opaque)
+{
+    dbus_timeout_handle(opaque);
+}
+
+static dbus_bool_t
+virtDBusWatchUpdate(DBusWatch *watch)
+{
+    _cleanup_(virtDBusUtilFreep) int *busWatch = dbus_watch_get_data(watch);
+    dbus_bool_t enabled = dbus_watch_get_enabled(watch);
+
+    if (enabled && !busWatch) {
+        busWatch = calloc(1, sizeof(int));
+        if (!busWatch)
+            return FALSE;
+
+        *busWatch = virEventAddHandle(dbus_watch_get_unix_fd(watch),
+                                      virtDBusGetLibvirtEvents(watch),
+                                      virtDBusHandleBusEvent,
+                                      watch,
+                                      NULL);
+        if (*busWatch < 0)
+            return FALSE;
+
+        dbus_watch_set_data(watch, busWatch, free);
+    } else if (!enabled && busWatch != NULL) {
+        virEventRemoveHandle(*busWatch);
+        dbus_watch_set_data(watch, NULL, NULL);
+    } else if (busWatch) {
+        virEventUpdateHandle(*busWatch, virtDBusGetLibvirtEvents(watch));
+    }
+
+    busWatch = NULL;
+    return TRUE;
+}
+
+static dbus_bool_t
+virtDBusAddWatch(DBusWatch *watch,
+                 void *data VIRT_ATTR_UNUSED)
+{
+    return virtDBusWatchUpdate(watch);
+}
+
+static void
+virtDBusUpdateWatch(DBusWatch *watch,
+                    void *data VIRT_ATTR_UNUSED)
+{
+    virtDBusWatchUpdate(watch);
+}
+
+static void
+virtDBusRemoveWatch(DBusWatch *watch,
+                    void *data VIRT_ATTR_UNUSED)
+{
+    int *busWatch;
+
+    busWatch = dbus_watch_get_data(watch);
+    if (busWatch)
+        virEventRemoveHandle(*busWatch);
+
+    dbus_watch_set_data(watch, NULL, NULL);
+}
+
+static dbus_bool_t
+virtDBusTimeoutUpdate(DBusTimeout *timeout)
+{
+    _cleanup_(virtDBusUtilFreep) int *busTimeout = dbus_timeout_get_data(timeout);
+    dbus_bool_t enabled = dbus_timeout_get_enabled(timeout);
+
+    if (enabled && !busTimeout) {
+        busTimeout = calloc(1, sizeof(int));
+        if (!busTimeout)
+            return FALSE;
+
+        *busTimeout = virEventAddTimeout(dbus_timeout_get_interval(timeout),
+                                         virtDBusHandleBusTimeout,
+                                         timeout,
+                                         NULL);
+        if (*busTimeout < 0)
+            return FALSE;
+
+        dbus_timeout_set_data(timeout, busTimeout, free);
+    } else if (!enabled && busTimeout != NULL) {
+        virEventRemoveTimeout(*busTimeout);
+        dbus_timeout_set_data(timeout, NULL, NULL);
+    } else if (busTimeout) {
+        virEventUpdateTimeout(*busTimeout, dbus_timeout_get_interval(timeout));
+    }
+
+    busTimeout = NULL;
+    return TRUE;
+}
+
+static dbus_bool_t
+virtDBusAddTimeout(DBusTimeout *timeout,
+                   void *data VIRT_ATTR_UNUSED)
+{
+    return virtDBusTimeoutUpdate(timeout);
+}
+
+static void
+virtDBusUpdateTimeout(DBusTimeout *timeout,
+                      void *data VIRT_ATTR_UNUSED)
+{
+    virtDBusTimeoutUpdate(timeout);
+}
+
+static void
+virtDBusRemoveTimeout(DBusTimeout *timeout,
+                      void *data VIRT_ATTR_UNUSED)
+{
+    int *busTimeout;
+
+    busTimeout = dbus_timeout_get_data(timeout);
+    if (busTimeout)
+        virEventRemoveTimeout(*busTimeout);
+
+    dbus_timeout_set_data(timeout, NULL, NULL);
+}
+
+/**
+ * virtDBusConnectionOpen:
+ * @busType which bus type to use for D-Bus connection
+ * @name name of the service to register
+ *
+ * Opens a D-Bus connection and registers necessary callbacks
+ * into Libvirt event loop.
+ *
+ * Returns D-Bus connection on success, NULL on failure.  The returned
+ * connection needs to be freed and closed using virtDBusConnectionClose().
+ */
+DBusConnection *
+virtDBusConnectionOpen(DBusBusType busType,
+                       const char *name)
+{
+    _cleanup_(dbus_error_free) DBusError error = DBUS_ERROR_INIT;
+    _cleanup_(virtDBusConnectionClose) DBusConnection *bus = NULL;
+    DBusConnection *ret;
+    int r;
+
+    if (!dbus_threads_init(NULL)) {
+        fprintf(stderr, "Failed to initialize dbus threads.\n");
+        return NULL;
+    }
+
+    bus = dbus_bus_get_private(busType, &error);
+
+    if (!bus) {
+        fprintf(stderr, "Failed to connect to session bus: %s\n", error.message);
+        return NULL;
+    }
+
+    r = dbus_bus_request_name(bus, name, DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
+    if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+        fprintf(stderr, "Failed to acquire service name: %s\n", error.message);
+        return NULL;
+    }
+
+    if (!dbus_connection_set_watch_functions(bus, virtDBusAddWatch,
+                                             virtDBusRemoveWatch,
+                                             virtDBusUpdateWatch,
+                                             dbus_connection_ref(bus),
+                                             (DBusFreeFunction)dbus_connection_unref)) {
+        fprintf(stderr, "Failed to register watch functions.\n");
+        return NULL;
+    }
+
+    if (!dbus_connection_set_timeout_functions(bus, virtDBusAddTimeout,
+                                               virtDBusRemoveTimeout,
+                                               virtDBusUpdateTimeout,
+                                               dbus_connection_ref(bus),
+                                               (DBusFreeFunction)dbus_connection_unref)) {
+        fprintf(stderr, "Failed to register timeout functions.\n");
+        return NULL;
+    }
+
+    VIRT_STEAL_PTR(ret, bus);
+    return ret;
+}
+
+static int
+virtDBusObjectListExpand(virtDBusObjectList *objectList,
+                         size_t len)
+{
+    virtDBusObject **newList;
+
+    newList = reallocarray(objectList->objects,
+                           objectList->len + len,
+                           sizeof(virtDBusObject *));
+    if (!newList)
+        return -1;
+
+    objectList->objects = newList;
+    objectList->len += len;
+    return 0;
+}
+
+/**
+ * virtDBusObjectListRegister:
+ * @objectList a list of objects
+ * @path object path of object preffix, depends on @type
+ * @introspectXML where to store object interface XML
+ * @interface name of the interface
+ * @type whether the @path is an object or prefix of an object
+ * @properties pointer to structure that lists all properties, can be NULL
+ * @methods pointer to structure that lists all methods, can be NULL
+ * @data user data that are passed to properties or methods handlers
+ *
+ * Registers a D-Bus object that we need to handle.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+virtDBusObjectListRegister(virtDBusObjectList *objectList,
+                           const char *path,
+                           const char **introspectXML,
+                           const char *interface,
+                           virtDBusObjectType type,
+                           virtDBusPropertyHandlers *properties,
+                           virtDBusMethodHandlers *methods,
+                           void *data)
+{
+    _cleanup_(virtDBusObjectFreep) virtDBusObject *newObject = NULL;
+
+    if (!objectList)
+        return -1;
+
+    newObject = calloc(1, sizeof(virtDBusObject));
+    if (!newObject)
+        return -1;
+
+    newObject->path = strdup(path);
+    if (!newObject->path)
+        return -1;
+
+    newObject->introspectXML = introspectXML;
+    newObject->interface = interface;
+    newObject->type = type;
+    newObject->properties = properties;
+    newObject->methods = methods;
+    newObject->data = data;
+
+    if (virtDBusObjectListExpand(objectList, 1) < 0)
+        return -1;
+
+    objectList->objects[objectList->len -1] = newObject;
+    newObject = NULL;
+
+    return 0;
+}
+
+static void
+virtDBusMessageUnref(DBusMessage **messagep)
+{
+    if (*messagep)
+        dbus_message_unref(*messagep);
+}
+
+static void
+virtDBusMessageIterFreep(DBusMessageIter **iterp)
+{
+    if (*iterp)
+        free(*iterp);
+}
+
+static DBusMessageIter *
+virtDBusMessageIterPop(virtDBusMessage *msg)
+{
+    DBusMessageIter *iter;
+    size_t pos;
+
+    if (msg->items == 0)
+        return NULL;
+
+    pos = msg->items - 1;
+
+    VIRT_STEAL_PTR(iter, msg->stack[pos]);
+
+    msg->items -= 1;
+
+    return iter;
+}
+
+static DBusMessageIter *
+virtDBusMessageIterPeek(virtDBusMessage *msg)
+{
+    if (msg->items == 0)
+        return NULL;
+
+    return msg->stack[msg->items - 1];
+}
+
+static int
+virtDBusMessageIterPush(virtDBusMessage *msg,
+                        DBusMessageIter *iter)
+{
+    DBusMessageIter **newStack = NULL;
+
+    if (msg->items >= msg->size) {
+        newStack = reallocarray(msg->stack, msg->items + 1,
+                                sizeof(DBusMessageIter *));
+        if (!newStack)
+            return -1;
+
+        msg->stack = newStack;
+        msg->size += 1;
+    }
+
+    msg->stack[msg->items] = iter;
+    msg->items += 1;
+
+    return 0;
+}
+
+static virtDBusMessage *
+virtDBusMessageNew(DBusMessage *message,
+                   DBusConnection *bus,
+                   DBusError *error)
+{
+    _cleanup_(virtDBusMessageFreep) virtDBusMessage *new = NULL;
+    _cleanup_(virtDBusMessageIterFreep) DBusMessageIter *iter = NULL;
+    virtDBusMessage *ret;
+
+    new = calloc(1, sizeof(virtDBusMessage));
+    if (!new)
+        return NULL;
+
+    new->message = dbus_message_ref(message);
+    new->bus = bus;
+    new->error = error;
+
+    iter = calloc(1, sizeof(DBusMessageIter));
+    if (!iter)
+        return NULL;
+
+    dbus_message_iter_init(new->message, iter);
+
+    if (virtDBusMessageIterPush(new, iter) < 0)
+        return NULL;
+
+    iter = NULL;
+
+    VIRT_STEAL_PTR(ret, new);
+    return ret;
+}
+
+/**
+ * virtDBusMessageMethodReturn:
+ * @msg: D-Bus message that we would like to reply to.
+ *
+ * Creates a new D-Bus message that will be send as reply to a method call.
+ *
+ * Returns new D-Bus message on success, NULL on failure.  The returned
+ * message needs to be freed using virtDBusMessageFreep().
+ */
+virtDBusMessage *
+virtDBusMessageMethodReturn(virtDBusMessage *msg)
+{
+    _cleanup_(virtDBusMessageFreep) virtDBusMessage *new = NULL;
+    _cleanup_(virtDBusMessageIterFreep) DBusMessageIter *iter = NULL;
+    virtDBusMessage *ret;
+
+    new = calloc(1, sizeof(virtDBusMessage));
+    if (!new)
+        return NULL;
+
+    new->message = dbus_message_new_method_return(msg->message);
+    if (!new->message)
+        return NULL;
+
+    new->bus = msg->bus;
+    new->error = msg->error;
+
+    iter = calloc(1, sizeof(DBusMessageIter));
+    if (!iter)
+        return NULL;
+
+    dbus_message_iter_init_append(new->message, iter);
+
+    if (virtDBusMessageIterPush(new, iter) < 0)
+        return NULL;
+
+    iter = NULL;
+
+    VIRT_STEAL_PTR(ret, new);
+    return ret;
+}
+
+/**
+ * virtDBusMessageNewSignal:
+ * @bus: D-Bus connection where we want to send the signal to
+ * @path: an object path to which this signal is related
+ * @interface: interface of the object
+ * @signal: a signal name
+ *
+ * Creates a new D-Bus message that will be send to specified D-Bus
+ * connection as a signal.
+ *
+ * Returns new D-Bus message on success, NULL on failure.  The returned
+ * message needs to be freed using virtDBusMessageFreep().
+ */
+virtDBusMessage *
+virtDBusMessageNewSignal(DBusConnection *bus,
+                         const char *path,
+                         const char *interface,
+                         const char *signal)
+{
+    _cleanup_(virtDBusMessageFreep) virtDBusMessage *new = NULL;
+    _cleanup_(virtDBusMessageIterFreep) DBusMessageIter *iter = NULL;
+    virtDBusMessage *ret;
+
+    new = calloc(1, sizeof(virtDBusMessage));
+    if (!new)
+        return NULL;
+
+    new->message = dbus_message_new_signal(path, interface, signal);
+    if (!new->message)
+        return NULL;
+
+    new->bus = bus;
+
+    iter = calloc(1, sizeof(DBusMessageIter));
+    if (!iter)
+        return NULL;
+
+    dbus_message_iter_init_append(new->message, iter);
+
+    if (virtDBusMessageIterPush(new, iter) < 0)
+        return NULL;
+
+    iter = NULL;
+
+    VIRT_STEAL_PTR(ret, new);
+    return ret;
+}
+
+/**
+ * virtDBusMessageAppendBasic:
+ * @msg: D-Bus reply message where to append the @value
+ * @type: type of the appended @value
+ * @value: pointer to a variable where the value is stored
+ *
+ * Appends a basic value into the reply message.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+virtDBusMessageAppendBasic(virtDBusMessage *msg,
+                           int type,
+                           const void *value)
+{
+    DBusMessageIter *iter = virtDBusMessageIterPeek(msg);
+
+    if (!iter)
+        return -1;
+
+    if (!dbus_message_iter_append_basic(iter, type, value))
+        return -1;
+
+    return 0;
+}
+
+/**
+ * virtDBusMessageReadBasic:
+ * @msg: D-Bus message that we would like to parse
+ * @type: type of the value the we expect to read
+ * @value: pointer to a variable where we want to store the @value
+ *
+ * Reads a basic value from incoming D-Bus message and stores it
+ * into provided variable.
+ *
+ * Returns 0 on success if there is no value left for reading,
+ * 1 on success if value was read and -1 on failure.  If used
+ * to read values from array 0 indicates that there are no other
+ * values left in the array.
+ */
+int
+virtDBusMessageReadBasic(virtDBusMessage *msg,
+                         int type,
+                         void *value)
+{
+    DBusMessageIter *iter = virtDBusMessageIterPeek(msg);
+    int msgType;
+
+    if (!iter)
+        return -1;
+
+    msgType = dbus_message_iter_get_arg_type(iter);
+    if (msg->items > 1 && msgType == DBUS_TYPE_INVALID)
+        return 0;
+
+    if (msgType != type) {
+        return virtDBusMessageSetError(msg, DBUS_ERROR_INVALID_ARGS,
+                                       "Expecting argument of type '%c', "
+                                       "but is actually of type '%c'.",
+                                       type, msgType);
+    }
+
+    dbus_message_iter_get_basic(iter, value);
+
+    dbus_message_iter_next(iter);
+
+    return 1;
+}
+
+/**
+ * virtDBusMessageOpenContainer:
+ * @msg: D-Bus reply message where to create a new container
+ * @type: type of the container that we are about to create
+ * @sig: signature of the container's content
+ *
+ * Creates a new container in the reply message, this needs to be
+ * followed by virtDBusMessageCloseContainer() when all values
+ * were written into it.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+virtDBusMessageOpenContainer(virtDBusMessage *msg,
+                             int type,
+                             const char *sig)
+{
+    DBusMessageIter *iter = virtDBusMessageIterPeek(msg);
+    _cleanup_(virtDBusMessageIterFreep) DBusMessageIter *cont = NULL;
+
+    if (!iter)
+        return -1;
+
+    cont = calloc(1, sizeof(DBusMessageIter));
+    if (!cont)
+        return -1;
+
+    if (!dbus_message_iter_open_container(iter, type, sig, cont))
+        return -1;
+
+    if (virtDBusMessageIterPush(msg, cont) < 0)
+        return -1;
+
+    cont = NULL;
+
+    return 0;
+}
+
+/**
+ * virtDBusMessageEnterContainer:
+ * @msg: D-Bus message that we would like to parse
+ * @type: type of the container the we expect to read
+ * @sig: signature of the container's content
+ *
+ * Enters into a container from the message that we are parsing.
+ * When we are done reading the container we need to leave it using
+ * virtDBusMessageExitContainer().
+ *
+ * Returns 0 on success if there is no container left for reading,
+ * 1 on success if container was entered and -1 on failure.  If used
+ * to enter into container from array 0 indicates that there are no
+ * other containers left in the array.
+ */
+int
+virtDBusMessageEnterContainer(virtDBusMessage *msg,
+                              int type,
+                              const char *sig)
+{
+    DBusMessageIter *iter = virtDBusMessageIterPeek(msg);
+    _cleanup_(virtDBusMessageIterFreep) DBusMessageIter *cont = NULL;
+    _cleanup_(virtDBusUtilFreep) char *msgSig = NULL;
+    int msgType;
+
+    if (!iter)
+        return -1;
+
+    msgType = dbus_message_iter_get_arg_type(iter);
+    if (msg->items > 1 && msgType == DBUS_TYPE_INVALID)
+        return 0;
+
+    if (msgType != type) {
+        return virtDBusMessageSetError(msg, DBUS_ERROR_INVALID_ARGS,
+                                       "Expecting argument of type '%c', "
+                                       "but is actually of type '%c'.",
+                                       type, msgType);
+    }
+
+
+    cont = calloc(1, sizeof(DBusMessageIter));
+    if (!cont)
+        return -1;
+
+    dbus_message_iter_recurse(iter, cont);
+
+    msgSig = dbus_message_iter_get_signature(cont);
+
+    if (strcmp(sig, msgSig) != 0) {
+        return virtDBusMessageSetError(msg, DBUS_ERROR_INVALID_ARGS,
+                                       "Invalid container signature '%s', "
+                                       "expected signature '%s'.",
+                                       sig, msgSig);
+    }
+
+    if (virtDBusMessageIterPush(msg, cont) < 0)
+        return -1;
+
+    cont = NULL;
+
+    return 1;
+}
+
+/**
+ * virtDBusMessageCloseContainer:
+ * @msg: D-Bus reply message where we are done creating new container
+ *
+ * Closes a newly created container in a reply message.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+virtDBusMessageCloseContainer(virtDBusMessage *msg)
+{
+    _cleanup_(virtDBusMessageIterFreep) DBusMessageIter *cont = virtDBusMessageIterPop(msg);
+    DBusMessageIter *iter = virtDBusMessageIterPeek(msg);
+
+    if (!cont || !iter)
+        return -1;
+
+    if (!dbus_message_iter_close_container(iter, cont))
+        return -1;
+
+    return 0;
+}
+
+/**
+ * virtDBusMessageExitContainer:
+ * @msg: D-Bus message that we are currently parsing
+ *
+ * Leaves a container that was opened for parsing.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+virtDBusMessageExitContainer(virtDBusMessage *msg)
+{
+    _cleanup_(virtDBusMessageIterFreep) DBusMessageIter *cont = virtDBusMessageIterPop(msg);
+    DBusMessageIter *iter = virtDBusMessageIterPeek(msg);
+
+    if (!cont || !iter)
+        return -1;
+
+    dbus_message_iter_next(iter);
+
+    return 0;
+}
+
+/**
+ * virtDBusMessagePeekType:
+ * @msg: D-Bus message that we are currently parsing
+ * @type: pointer to a variable where to store the value type
+ *
+ * Gets a @type of a first value stored in a container.  This is
+ * mainly used get a type of a value stored in VARIANT container.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+virtDBusMessagePeekType(virtDBusMessage *msg,
+                        int *type)
+{
+    DBusMessageIter *iter = virtDBusMessageIterPeek(msg);
+    DBusMessageIter cont;
+    int msgType;
+
+    if (!iter)
+        return -1;
+
+    msgType = dbus_message_iter_get_arg_type(iter);
+    if (msgType != DBUS_TYPE_ARRAY &&
+        msgType != DBUS_TYPE_VARIANT &&
+        msgType != DBUS_TYPE_STRUCT &&
+        msgType != DBUS_TYPE_DICT_ENTRY) {
+        return virtDBusMessageSetError(msg, DBUS_ERROR_INVALID_ARGS,
+                                       "Expecting container type, "
+                                       "but is actually of type '%c'.",
+                                       msgType);
+    }
+
+    dbus_message_iter_recurse(iter, &cont);
+    *type = dbus_message_iter_get_arg_type(&cont);
+
+    return 0;
+}
+
+/**
+ * virtDBusMessageAppendVariant:
+ * @msg: D-Bus reply message where to append the variant value
+ * @type: type of the value stored in variant container
+ * @sig: signature of the value stored in variant container
+ * @value: pointer to a variable where the @value is stored
+ *
+ * Appends a variant container together with the value into the
+ * reply message.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+virtDBusMessageAppendVariant(virtDBusMessage *msg,
+                             int type,
+                             const char *sig,
+                             const void *value)
+{
+    if (virtDBusMessageOpenContainer(msg, DBUS_TYPE_VARIANT, sig) < 0)
+        return -1;
+
+    if (virtDBusMessageAppendBasic(msg, type, value) < 0)
+        return -1;
+
+    return virtDBusMessageCloseContainer(msg);
+}
+
+/**
+ * virtDBusMessageReadVariant:
+ * @msg: D-Bus message that we are currently parsing
+ * @type: expected type of the @value stored in variant
+ * @sig: signature of the expected @value
+ * @value: pointer to a variable where to store the @value
+ *
+ * Reads a variant container together with the value from incoming
+ * message.  It is necessary to know the value stored in the variant,
+ * virtDBusMessagePeekType() can be used to get the type.
+ *
+ * Returns 0 on success if there is no variant left for reading,
+ * 1 on success if variant was read and -1 on failure.  If used
+ * to read variant value from array 0 indicates that there are no
+ * other variants left in the array.
+ */
+int
+virtDBusMessageReadVariant(virtDBusMessage *msg,
+                           int type,
+                           const char *sig,
+                           void *value)
+{
+    int r;
+
+    r = virtDBusMessageEnterContainer(msg, DBUS_TYPE_VARIANT, sig);
+    if (r < 0)
+        return -1;
+    if (r == 0)
+        return 0;
+
+    if (virtDBusMessageReadBasic(msg, type, value) < 0)
+        return -1;
+
+    return virtDBusMessageExitContainer(msg);
+}
+
+const char *
+virtDBusMessageGetPath(virtDBusMessage *msg)
+{
+    if (msg && msg->message)
+        return dbus_message_get_path(msg->message);
+    return NULL;
+}
+
+const char *
+virtDBusMessageGetInterface(virtDBusMessage *msg)
+{
+    if (msg && msg->message)
+        return dbus_message_get_interface(msg->message);
+    return NULL;
+}
+
+const char *
+virtDBusMessageGetMember(virtDBusMessage *msg)
+{
+    if (msg && msg->message)
+        return dbus_message_get_member(msg->message);
+    return NULL;
+}
+
+int
+virtDBusMessageGetType(virtDBusMessage *msg)
+{
+    if (msg && msg->message)
+        return dbus_message_get_type(msg->message);
+    return -1;
+}
+
+int
+virtDBusMessageSetError(virtDBusMessage *msg,
+                        const char *name,
+                        const char *format,
+                        ...)
+{
+    _cleanup_(virtDBusUtilFreep) char *error = NULL;
+    va_list ap;
+    int r;
+
+    if (!msg->error)
+        return -1;
+
+    va_start(ap, format);
+    r = vasprintf(&error, format, ap);
+    va_end(ap);
+    if (r < 0)
+        return -1;
+
+    dbus_set_error(msg->error, name, "%s", error);
+
+    return -1;
+}
+
+int
+virtDBusSendMessage(virtDBusMessage *msg)
+{
+    dbus_connection_send(msg->bus, msg->message, NULL);
+    return 0;
+}
+
+int
+virtDBusSendEmptyMessage(virtDBusMessage *msg)
+{
+    _cleanup_(virtDBusMessageFreep) virtDBusMessage *reply = NULL;
+
+    reply = virtDBusMessageMethodReturn(msg);
+    if (!reply)
+        return -1;
+
+    return virtDBusSendMessage(reply);
+}
+
+static int
+virtDBusHandleIntrospect(virtDBusMessage *msg,
+                         virtDBusObject *object)
+{
+    _cleanup_(virtDBusMessageFreep) virtDBusMessage *reply = NULL;
+
+    if (!*object->introspectXML)
+        *object->introspectXML = virtDBusUtilLoadIntrospect(object->interface);
+
+    if (!*object->introspectXML) {
+        return virtDBusMessageSetError(msg, DBUS_ERROR_FILE_NOT_FOUND,
+                                       "missing introspect data");
+    }
+
+    reply = virtDBusMessageMethodReturn(msg);
+    if (!reply)
+        return -1;
+
+    if (virtDBusMessageAppendBasic(reply, DBUS_TYPE_STRING,
+                                   object->introspectXML) < 0)
+        return -1;
+
+    virtDBusSendMessage(reply);
+    return 0;
+}
+
+static int
+virtDBusHandlePropertyGet(virtDBusMessage *msg,
+                          virtDBusObject *object)
+{
+    _cleanup_(virtDBusMessageFreep) virtDBusMessage *reply = NULL;
+    virtDBusPropertyGetHandler callback = NULL;
+    const char *property;
+    const char *interface;
+
+    if (virtDBusMessageReadBasic(msg, DBUS_TYPE_STRING, &interface) < 0)
+        return -1;
+
+    if (virtDBusMessageReadBasic(msg, DBUS_TYPE_STRING, &property) < 0)
+        return -1;
+
+    for (int i = 0; object->properties[i].property != NULL; i++) {
+        if (strcmp(object->properties[i].property, property) == 0) {
+            callback = object->properties[i].get;
+            break;
+        }
+    }
+
+    if (!callback) {
+        return virtDBusMessageSetError(msg, DBUS_ERROR_UNKNOWN_PROPERTY,
+                                       "unknown property '%s'", property);
+    }
+
+    reply = virtDBusMessageMethodReturn(msg);
+    if (!reply)
+        return -1;
+
+    if (callback(reply, msg, interface, property, object->data) < 0)
+        return -1;
+
+    virtDBusSendMessage(reply);
+    return 0;
+}
+
+static int
+virtDBusHandlePropertySet(virtDBusMessage *msg,
+                          virtDBusObject *object)
+{
+    _cleanup_(virtDBusMessageFreep) virtDBusMessage *reply = NULL;
+    virtDBusPropertySetHandler callback = NULL;
+    const char *property;
+    const char *interface;
+
+    if (virtDBusMessageReadBasic(msg, DBUS_TYPE_STRING, &interface) < 0)
+        return -1;
+
+    if (virtDBusMessageReadBasic(msg, DBUS_TYPE_STRING, &property) < 0)
+        return -1;
+
+
+    for (int i = 0; object->properties[i].property != NULL; i++) {
+        if (strcmp(object->properties[i].property, property) == 0) {
+            callback = object->properties[i].set;
+            break;
+        }
+    }
+
+    if (!callback) {
+        return virtDBusMessageSetError(msg, DBUS_ERROR_UNKNOWN_PROPERTY,
+                                       "unknown property '%s'", property);
+    }
+
+    if (callback(msg, interface, property, object->data) < 0)
+        return -1;
+
+    reply = virtDBusMessageMethodReturn(msg);
+    if (!reply)
+        return -1;
+
+    virtDBusSendMessage(reply);
+    return 0;
+}
+
+static int
+virtDBusHandlePropertyGetAll(virtDBusMessage *msg,
+                             virtDBusObject *object)
+{
+    _cleanup_(virtDBusMessageFreep) virtDBusMessage *reply = NULL;
+    const char *interface;
+
+    if (virtDBusMessageReadBasic(msg, DBUS_TYPE_STRING, &interface) < 0)
+        return -1;
+
+    reply = virtDBusMessageMethodReturn(msg);
+    if (!reply)
+        return -1;
+
+    if (virtDBusMessageOpenContainer(reply, DBUS_TYPE_ARRAY, "{sv}") < 0)
+        return -1;
+
+    for (int i = 0; object->properties[i].property != NULL; i++) {
+        if (virtDBusMessageOpenContainer(reply, DBUS_TYPE_DICT_ENTRY, NULL) < 0)
+            return -1;
+
+        if (virtDBusMessageAppendBasic(reply, DBUS_TYPE_STRING,
+                                       &object->properties[i].property) < 0) {
+            return -1;
+        }
+
+        if (object->properties[i].get(reply, msg, interface,
+                                      object->properties[i].property,
+                                      object->data) < 0) {
+            return -1;
+        }
+
+        if (virtDBusMessageCloseContainer(reply) < 0)
+            return -1;
+    }
+
+    if (virtDBusMessageCloseContainer(reply) < 0)
+        return -1;
+
+    virtDBusSendMessage(reply);
+    return 0;
+}
+
+static int
+virtDBusHandleProperty(virtDBusMessage *msg,
+                       virtDBusObject *object)
+{
+    const char *member = virtDBusMessageGetMember(msg);
+
+    if (strcmp(member, "Get") == 0) {
+        return virtDBusHandlePropertyGet(msg, object);
+    } else if (strcmp(member, "Set") == 0) {
+        return virtDBusHandlePropertySet(msg, object);
+    } else if (strcmp(member, "GetAll") == 0) {
+        return virtDBusHandlePropertyGetAll(msg, object);
+    } else {
+        return virtDBusMessageSetError(msg, DBUS_ERROR_UNKNOWN_METHOD,
+                                       "unknown method '%s'", member);
+    }
+}
+
+static int
+virtDBusHandleMethod(virtDBusMessage *msg,
+                     virtDBusObject *object)
+{
+    const char *member = virtDBusMessageGetMember(msg);
+    const char *msgSig = dbus_message_get_signature(msg->message);
+    virtDBusMethodHandlers *handle = NULL;
+
+    for (int i = 0; object->methods[i].method != NULL; i++) {
+        if (strcmp(member, object->methods[i].method) == 0) {
+            handle = (object->methods) + i;
+            break;
+        }
+    }
+
+    if (!handle) {
+        return virtDBusMessageSetError(msg, DBUS_ERROR_UNKNOWN_METHOD,
+                                       "unknown method '%s'", member);
+    }
+
+    if (strcmp(handle->signature, msgSig) != 0)
+        return virtDBusMessageSetError(msg, DBUS_ERROR_INVALID_ARGS,
+                                       "Invalid message signature '%s', "
+                                       "expected signature '%s'.",
+                                       msgSig, handle->signature);
+    return handle->callback(msg, object->data);
+}
+
+static int
+virtDBusHandleError(virtDBusMessage *msg)
+{
+    _cleanup_(virtDBusMessageUnref) DBusMessage *reply = NULL;
+
+    if (!dbus_error_is_set(msg->error))
+        return -1;
+
+    reply = dbus_message_new_error(msg->message,
+                                   msg->error->name,
+                                   msg->error->message);
+    if (!reply)
+        return -1;
+
+    dbus_connection_send(msg->bus, reply, NULL);
+    return 1;
+}
+
+int
+virtDBusDispatchMessage(DBusConnection *bus,
+                        virtDBusObjectList *objectList)
+{
+    _cleanup_(virtDBusMessageUnref) DBusMessage *message = NULL;
+    _cleanup_(virtDBusMessageFreep) virtDBusMessage *msg = NULL;
+    _cleanup_(dbus_error_free) DBusError error = DBUS_ERROR_INIT;
+    virtDBusObject *object = NULL;
+    const char *path;
+    const char *interface;
+    int r;
+
+    message = dbus_connection_pop_message(bus);
+    if (!message)
+        return 0;
+
+    if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
+        return 1;
+
+    msg = virtDBusMessageNew(message, bus, &error);
+    if (!msg)
+        return -1;
+
+    path = virtDBusMessageGetPath(msg);
+
+    for (int i = 0; i < objectList->len; i++) {
+        virtDBusObject *tmpObject = objectList->objects[i];
+        if ((tmpObject->type == VIRT_DBUS_OBJECT_TYPE_MATCH &&
+             strcmp(path, tmpObject->path) == 0) ||
+            (tmpObject->type == VIRT_DBUS_OBJECT_TYPE_PREFIX &&
+             strstr(path, tmpObject->path) != NULL)) {
+            object = tmpObject;
+            break;
+        }
+    }
+
+    if (!object) {
+        virtDBusMessageSetError(msg, DBUS_ERROR_UNKNOWN_OBJECT,
+                                "invalid object '%s'", path);
+        return virtDBusHandleError(msg);
+    }
+
+    interface = virtDBusMessageGetInterface(msg);
+    if (!interface) {
+        virtDBusMessageSetError(msg, DBUS_ERROR_UNKNOWN_INTERFACE,
+                                "missing interface");
+        return virtDBusHandleError(msg);
+    }
+
+    if (strcmp(interface, DBUS_INTERFACE_INTROSPECTABLE) == 0) {
+        r = virtDBusHandleIntrospect(msg, object);
+    } else if (strcmp(interface, DBUS_INTERFACE_PROPERTIES) == 0) {
+        r = virtDBusHandleProperty(msg, object);
+    } else if (strcmp(interface, object->interface) == 0) {
+        r = virtDBusHandleMethod(msg, object);
+    } else {
+        virtDBusMessageSetError(msg, DBUS_ERROR_UNKNOWN_INTERFACE,
+                                "invalid interface '%s'", interface);
+        return virtDBusHandleError(msg);
+    }
+
+    if (r < 0) {
+        return virtDBusHandleError(msg);
+    }
+
+    return 1;
+}
+
+void
+virtDBusMessageFreep(virtDBusMessage **msgp)
+{
+    if (*msgp) {
+        virtDBusMessage *msg = *msgp;
+
+        for (int i = 0; i < msg->items; i++) {
+            if (msg->stack[i])
+                free(msg->stack[i]);
+        }
+        free(msg->stack);
+
+        if (msg->message)
+            dbus_message_unref(msg->message);
+
+        free(*msgp);
+    }
+}
+
+void
+virtDBusConnectionClose(DBusConnection **bus)
+{
+    if (*bus) {
+        dbus_connection_close(*bus);
+        dbus_connection_unref(*bus);
+    }
+}
+
+static void
+virtDBusObjectFree(virtDBusObject *object)
+{
+    free(object->path);
+    free(object);
+}
+
+void
+virtDBusObjectFreep(virtDBusObject **objectp)
+{
+    if (*objectp)
+        virtDBusObjectFree(*objectp);
+}
+
+void
+virtDBusObjectListFree(virtDBusObjectList *list)
+{
+    for (int i = 0; i < list->len; i++)
+        virtDBusObjectFree(list->objects[i]);
+    free(list->objects);
+}
diff --git a/src/dbus.h b/src/dbus.h
new file mode 100644
index 0000000..14d15d2
--- /dev/null
+++ b/src/dbus.h
@@ -0,0 +1,158 @@
+#pragma once
+
+#define VIR_ENUM_SENTINELS
+
+#include <dbus/dbus.h>
+#include <libvirt/libvirt.h>
+
+typedef struct _virtDBusMessage virtDBusMessage;
+
+typedef int
+(*virtDBusPropertyGetHandler)(virtDBusMessage *reply,
+                              virtDBusMessage *msg,
+                              const char *interface,
+                              const char *property,
+                              void *data);
+
+typedef int
+(*virtDBusPropertySetHandler)(virtDBusMessage *msg,
+                              const char *interface,
+                              const char *property,
+                              void *data);
+
+struct _virtDBusPropertyHandlers {
+    const char *property;
+    virtDBusPropertyGetHandler get;
+    virtDBusPropertySetHandler set;
+};
+typedef struct _virtDBusPropertyHandlers virtDBusPropertyHandlers;
+
+typedef int
+(*virtDBusMethodHandler)(virtDBusMessage* msg,
+                         void *data);
+
+struct _virtDBusMethodHandlers {
+    const char *method;
+    const char *signature;
+    virtDBusMethodHandler callback;
+};
+typedef struct _virtDBusMethodHandlers virtDBusMethodHandlers;
+
+typedef struct _virtDBusObject virtDBusObject;
+
+struct _virtDBusObjectList {
+    virtDBusObject **objects;
+    size_t len;
+};
+typedef struct _virtDBusObjectList virtDBusObjectList;
+
+DBusConnection *
+virtDBusConnectionOpen(DBusBusType busType,
+                       const char *name);
+
+typedef enum {
+    VIRT_DBUS_OBJECT_TYPE_MATCH,
+    VIRT_DBUS_OBJECT_TYPE_PREFIX,
+} virtDBusObjectType;
+
+int
+virtDBusObjectListRegister(virtDBusObjectList *objectList,
+                           const char *path,
+                           const char **introspectXML,
+                           const char *interface,
+                           virtDBusObjectType type,
+                           virtDBusPropertyHandlers *properties,
+                           virtDBusMethodHandlers *methods,
+                           void *data);
+
+int
+virtDBusDispatchMessage(DBusConnection *bus,
+                        virtDBusObjectList *objectList);
+
+virtDBusMessage *
+virtDBusMessageMethodReturn(virtDBusMessage *msg);
+
+virtDBusMessage *
+virtDBusMessageNewSignal(DBusConnection *bus,
+                         const char *path,
+                         const char *interface,
+                         const char *signal);
+
+int
+virtDBusMessageAppendBasic(virtDBusMessage *msg,
+                           int type,
+                           const void *value);
+
+int
+virtDBusMessageReadBasic(virtDBusMessage *msg,
+                         int type,
+                         void *value);
+
+int
+virtDBusMessageOpenContainer(virtDBusMessage *msg,
+                             int type,
+                             const char *sig);
+
+int
+virtDBusMessageCloseContainer(virtDBusMessage *msg);
+
+int
+virtDBusMessageEnterContainer(virtDBusMessage *msg,
+                              int type,
+                              const char *sig);
+
+int
+virtDBusMessageExitContainer(virtDBusMessage *msg);
+
+int
+virtDBusMessagePeekType(virtDBusMessage *msg,
+                        int *type);
+
+int
+virtDBusMessageAppendVariant(virtDBusMessage *msg,
+                             int type,
+                             const char *sig,
+                             const void *value);
+
+int
+virtDBusMessageReadVariant(virtDBusMessage *msg,
+                           int type,
+                           const char *sig,
+                           void *value);
+
+const char *
+virtDBusMessageGetPath(virtDBusMessage *msg);
+
+const char *
+virtDBusMessageGetInterface(virtDBusMessage *msg);
+
+const char *
+virtDBusMessageGetMember(virtDBusMessage *msg);
+
+int
+virtDBusMessageGetType(virtDBusMessage *msg);
+
+int
+virtDBusMessageSetError(virtDBusMessage *msg,
+                        const char *name,
+                        const char *format,
+                        ...)
+    __attribute__ ((format (printf, 3, 4)));
+
+int
+virtDBusSendMessage(virtDBusMessage *msg);
+
+int
+virtDBusSendEmptyMessage(virtDBusMessage *msg);
+
+void
+virtDBusMessageFreep(virtDBusMessage **msgp);
+
+void
+virtDBusConnectionClose(DBusConnection **bus);
+
+void
+virtDBusObjectFreep(virtDBusObject **objectp);
+
+void
+virtDBusObjectListFree(virtDBusObjectList *list);
diff --git a/src/domain.c b/src/domain.c
index f68a3a0..daff32d 100644
--- a/src/domain.c
+++ b/src/domain.c
@@ -515,14 +515,6 @@ virtDBusDomainLookup(sd_bus *bus VIRT_ATTR_UNUSED,
     virtDBusConnect *connect = userdata;
     _cleanup_(virtDBusUtilFreep) char *name = NULL;
     _cleanup_(virtDBusUtilVirDomainFreep) virDomainPtr domain = NULL;
-    int r;
-
-    r = sd_bus_path_decode(path, connect->domainPath, &name);
-    if (r < 0)
-        return r;
-
-    if (*name == '\0')
-        return 0;
 
     domain = virtDBusDomainGetVirDomain(connect, path, error);
     if (!domain)
diff --git a/src/util.c b/src/util.c
index 9042a0f..b6919fb 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1,9 +1,18 @@
+#define _GNU_SOURCE
+
 #include "util.h"
 
+#include <fcntl.h>
 #include <libvirt/virterror.h>
+#include <stdio.h>
 #include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
 
+#define VIRT_UTIL_READ_BLOCK_SIZE 1024
+#define VIRT_UTIL_FILE_LIMIT 1024 * 1024
+
 int
 virtDBusUtilMessageAppendTypedParameters(sd_bus_message *message,
                                          virTypedParameterPtr parameters,
@@ -78,15 +87,125 @@ virtDBusUtilSetError(sd_bus_error *error,
     return sd_bus_error_set(error, VIRT_DBUS_ERROR_INTERFACE, message);
 }
 
+char *
+virtDBusUtilReadFile(const char *filename)
+{
+    _cleanup_(virtDBusUtilFreep) char *data = NULL;
+    _cleanup_(virtDBusUtilClosep) int fd = -1;
+    char buf[VIRT_UTIL_READ_BLOCK_SIZE];
+    char *ret;
+    size_t len = 0;
+    ssize_t readlen;
+
+    fd = open(filename, O_RDONLY);
+    if (fd < 0)
+        return NULL;
+
+    while ((readlen = read(fd, buf, VIRT_UTIL_READ_BLOCK_SIZE)) > 0) {
+        len += readlen;
+
+        if (len > VIRT_UTIL_FILE_LIMIT)
+            return NULL;
+
+        data = realloc(data, len + 1);
+        if (!data)
+            return NULL;
+
+        memcpy(data+len-readlen, buf, readlen);
+    }
+
+    if (readlen < 0 || !data)
+        return NULL;
+
+    data[len] = 0;
+    VIRT_STEAL_PTR(ret, data);
+    return ret;
+}
+
+static const char *dbusInterfacePrefix = NULL;
+
+char *
+virtDBusUtilLoadIntrospect(const char *interface)
+{
+    _cleanup_(virtDBusUtilFreep) char *path = NULL;
+
+    if (!dbusInterfacePrefix) {
+        dbusInterfacePrefix = getenv("VIRT_DBUS_INTERFACES_DIR");
+        if (!dbusInterfacePrefix)
+            dbusInterfacePrefix = VIRT_DBUS_INTERFACES_DIR;
+    }
+
+    if (asprintf(&path, "%s/%s.xml", dbusInterfacePrefix, interface) < 0)
+        return NULL;
+
+    return virtDBusUtilReadFile(path);
+}
+
+static char *
+virtDBusUtilEncodeUUID(const char *uuid)
+{
+    char *newUuid = NULL;
+    size_t uuidLen = strlen(uuid);
+    int i;
+
+    newUuid = calloc(uuidLen + 2, sizeof(char));
+    if (!newUuid)
+        return NULL;
+
+    newUuid[0] = '_';
+
+    for (i = 0; i < uuidLen; i++) {
+        if (uuid[i] == '-')
+            newUuid[i + 1] = '_';
+        else
+            newUuid[i + 1] = uuid[i];
+    }
+
+    newUuid[i + 1] = 0;
+
+    return newUuid;
+}
+
+static char *
+virtDBusUtilDecodeUUID(const char *uuidEsc)
+{
+    char *uuid = NULL;
+    size_t uuidLen = strlen(uuidEsc);
+    int i;
+
+    if (uuidEsc[0] != '_')
+        return NULL;
+
+    uuid = calloc(uuidLen, sizeof(char));
+    if (!uuid)
+        return NULL;
+
+    for (i = 1; i < uuidLen; i++) {
+        if (uuidEsc[i] == '_')
+            uuid[i - 1] = '-';
+        else
+            uuid[i - 1] = uuidEsc[i];
+    }
+
+    uuid[uuidLen -1] = 0;
+
+    return uuid;
+}
+
 char *
 virtDBusUtilBusPathForVirDomain(virDomainPtr domain,
                                 const char *domainPath)
 {
     char *path = NULL;
+    _cleanup_(virtDBusUtilFreep) char *uuidEsc = NULL;
     char uuid[VIR_UUID_STRING_BUFLEN] = "";
 
     virDomainGetUUIDString(domain, uuid);
-    sd_bus_path_encode(domainPath, uuid, &path);
+
+    uuidEsc = virtDBusUtilEncodeUUID(uuid);
+
+    if (asprintf(&path, "%s/%s", domainPath, uuidEsc) < 0)
+        return NULL;
 
     return path;
 }
@@ -96,14 +215,14 @@ virtDBusUtilVirDomainFromBusPath(virConnectPtr connection,
                                  const char *path,
                                  const char *domainPath)
 {
-    _cleanup_(virtDBusUtilFreep) char *name = NULL;
-    int r;
+    _cleanup_(virtDBusUtilFreep) char *uuid = NULL;
+    size_t prefixLen = strlen(domainPath) + 1;
 
-    r = sd_bus_path_decode(path, domainPath, &name);
-    if (r < 0)
+    uuid = virtDBusUtilDecodeUUID(path+prefixLen);
+    if (!uuid)
         return NULL;
 
-    return virDomainLookupByUUIDString(connection, name);
+    return virDomainLookupByUUIDString(connection, uuid);
 }
 
 void
diff --git a/src/util.h b/src/util.h
index 2a6f7e2..c42d2cc 100644
--- a/src/util.h
+++ b/src/util.h
@@ -11,6 +11,11 @@
 
 #define VIRT_N_ELEMENTS(array) (sizeof(array) / sizeof(*(array)))
 
+#define VIRT_STEAL_PTR(dest, src) \
+    do { \
+        (dest) = (src); \
+        (src) = NULL; \
+    } while(0)
 
 int
 virtDBusUtilMessageAppendTypedParameters(sd_bus_message *message,
@@ -24,6 +29,12 @@ int
 virtDBusUtilSetError(sd_bus_error *error,
                      const char *message);
 
+char *
+virtDBusUtilReadFile(const char *filename);
+
+char *
+virtDBusUtilLoadIntrospect(const char *interface);
+
 char *
 virtDBusUtilBusPathForVirDomain(virDomainPtr domain,
                                 const char *domainPath);
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..47fe70c 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 libdbus-1-dev libtool python3-gi python3-dbus dbus
 
 # run build and tests as user
 chown -R buildd:buildd /build
-- 
2.14.3

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [dbus PATCH 16/18] introduce support for libdbus library
Posted by Daniel P. Berrangé 7 years, 2 months ago
On Mon, Mar 12, 2018 at 05:21:46PM +0100, Pavel Hrdina wrote:
> We will switch to libdbus library because the systemd sd-bus
> implementation is not thread safe.

Hmmm, libdbus.so isn't all that great with threads either. It has
had countless bugs in its thread support over the years to the
extent that DBus maintainers have actively discouraged people from
using it. GLib didn't even try to use it and wrote new bindings
straight to the socket protocol for this reason.

Personally I think it would be worth considering using glib for
libvirt-dbus. As well as getting access to a nicer DBus binding,
you get to take advantage of higher level APIs that GLib provides
compared to STDC / POSIX. The only real downside of using GLib
is abort-on-OOM behaviour, but I don't think that's an issue for
libvirt-dbus as its just an API shim layer.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [dbus PATCH 16/18] introduce support for libdbus library
Posted by Pavel Hrdina 7 years, 2 months ago
On Mon, Mar 12, 2018 at 04:30:30PM +0000, Daniel P. Berrangé wrote:
> On Mon, Mar 12, 2018 at 05:21:46PM +0100, Pavel Hrdina wrote:
> > We will switch to libdbus library because the systemd sd-bus
> > implementation is not thread safe.
> 
> Hmmm, libdbus.so isn't all that great with threads either. It has
> had countless bugs in its thread support over the years to the
> extent that DBus maintainers have actively discouraged people from
> using it. GLib didn't even try to use it and wrote new bindings
> straight to the socket protocol for this reason.

They still claim it in their documentation that you should use
different API if possible but if you really want to you can use
libdbus directly but the only drawback from that claim is that
the API is low-level and hard to use.

Another claim from their documentation is that DBusConnection is thread
safe which is the only thing that libvirt-dbus requires to be thread
safe, they specifically say that DBusMessage doesn't have any thread
locks, but that's also OK since the DBusMessage will live only in one
thread.

I don't have any experience using libdbus (except this one) so if
threading is still not reliable and safe then it would be probably
better to use different API.  I've done some research about libdbus
before using it and yes, there ware some articles that it was broken
in the past, however nothing recent.

> Personally I think it would be worth considering using glib for
> libvirt-dbus. As well as getting access to a nicer DBus binding,
> you get to take advantage of higher level APIs that GLib provides
> compared to STDC / POSIX. The only real downside of using GLib
> is abort-on-OOM behaviour, but I don't think that's an issue for
> libvirt-dbus as its just an API shim layer.

I personally don't like GLib that much, but I can give it a try and
prepare alternative patches with GLib.

Pavel
--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [dbus PATCH 16/18] introduce support for libdbus library
Posted by Daniel P. Berrangé 7 years, 2 months ago
On Tue, Mar 13, 2018 at 09:39:13AM +0100, Pavel Hrdina wrote:
> On Mon, Mar 12, 2018 at 04:30:30PM +0000, Daniel P. Berrangé wrote:
> > On Mon, Mar 12, 2018 at 05:21:46PM +0100, Pavel Hrdina wrote:
> > > We will switch to libdbus library because the systemd sd-bus
> > > implementation is not thread safe.
> > 
> > Hmmm, libdbus.so isn't all that great with threads either. It has
> > had countless bugs in its thread support over the years to the
> > extent that DBus maintainers have actively discouraged people from
> > using it. GLib didn't even try to use it and wrote new bindings
> > straight to the socket protocol for this reason.
> 
> They still claim it in their documentation that you should use
> different API if possible but if you really want to you can use
> libdbus directly but the only drawback from that claim is that
> the API is low-level and hard to use.

Yes, it is certainly low level - when I wrote the Perl binding I ended
up creating a much higher level wrapper around libdbus to make it
easier to use - similar to how GLib GIO DBus  has the low level and
high level APIs. One deals with messages, the other deals with objects
and methods/properties.

> Another claim from their documentation is that DBusConnection is thread
> safe which is the only thing that libvirt-dbus requires to be thread
> safe, they specifically say that DBusMessage doesn't have any thread
> locks, but that's also OK since the DBusMessage will live only in one
> thread.
> 
> I don't have any experience using libdbus (except this one) so if
> threading is still not reliable and safe then it would be probably
> better to use different API.  I've done some research about libdbus
> before using it and yes, there ware some articles that it was broken
> in the past, however nothing recent.

Yeah, most recent I can find is

https://bugs.freedesktop.org/show_bug.cgi?id=54972

which suggest the core problems were all solved, but the bug is
not marked as fixed so I'm unclear what issues remain.

> > Personally I think it would be worth considering using glib for
> > libvirt-dbus. As well as getting access to a nicer DBus binding,
> > you get to take advantage of higher level APIs that GLib provides
> > compared to STDC / POSIX. The only real downside of using GLib
> > is abort-on-OOM behaviour, but I don't think that's an issue for
> > libvirt-dbus as its just an API shim layer.
> 
> I personally don't like GLib that much, but I can give it a try and
> prepare alternative patches with GLib.

On second glance I'm unclear the recommended way to deal with GLib
DBus APIs when you have long running APIs to invoke. I get the feeling
that you would end up using GTask to spawn single-use threads to execute
the API call, but keeping all dbus interaction in the main thread.
This would likely make it desirable to use the libvirt-gobject library
at the same time to get easier access to async libvirt API execution.

I've copied you on a mail to the DBus mailing list to see if we can get
some guidance from people with more up2date knowledge than me, since
I've not been actively involved in dbus community for a little while now.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [dbus PATCH 16/18] introduce support for libdbus library
Posted by Daniel P. Berrangé 7 years, 2 months ago
On Tue, Mar 13, 2018 at 10:07:58AM +0000, Daniel P. Berrangé wrote:
> On Tue, Mar 13, 2018 at 09:39:13AM +0100, Pavel Hrdina wrote:
> > On Mon, Mar 12, 2018 at 04:30:30PM +0000, Daniel P. Berrangé wrote:
> > > On Mon, Mar 12, 2018 at 05:21:46PM +0100, Pavel Hrdina wrote:
> > > > We will switch to libdbus library because the systemd sd-bus
> > > > implementation is not thread safe.
> > > 
> > > Hmmm, libdbus.so isn't all that great with threads either. It has
> > > had countless bugs in its thread support over the years to the
> > > extent that DBus maintainers have actively discouraged people from
> > > using it. GLib didn't even try to use it and wrote new bindings
> > > straight to the socket protocol for this reason.
> > 
> > They still claim it in their documentation that you should use
> > different API if possible but if you really want to you can use
> > libdbus directly but the only drawback from that claim is that
> > the API is low-level and hard to use.
> 
> Yes, it is certainly low level - when I wrote the Perl binding I ended
> up creating a much higher level wrapper around libdbus to make it
> easier to use - similar to how GLib GIO DBus  has the low level and
> high level APIs. One deals with messages, the other deals with objects
> and methods/properties.
> 
> > Another claim from their documentation is that DBusConnection is thread
> > safe which is the only thing that libvirt-dbus requires to be thread
> > safe, they specifically say that DBusMessage doesn't have any thread
> > locks, but that's also OK since the DBusMessage will live only in one
> > thread.
> > 
> > I don't have any experience using libdbus (except this one) so if
> > threading is still not reliable and safe then it would be probably
> > better to use different API.  I've done some research about libdbus
> > before using it and yes, there ware some articles that it was broken
> > in the past, however nothing recent.
> 
> Yeah, most recent I can find is
> 
> https://bugs.freedesktop.org/show_bug.cgi?id=54972
> 
> which suggest the core problems were all solved, but the bug is
> not marked as fixed so I'm unclear what issues remain.
> 
> > > Personally I think it would be worth considering using glib for
> > > libvirt-dbus. As well as getting access to a nicer DBus binding,
> > > you get to take advantage of higher level APIs that GLib provides
> > > compared to STDC / POSIX. The only real downside of using GLib
> > > is abort-on-OOM behaviour, but I don't think that's an issue for
> > > libvirt-dbus as its just an API shim layer.
> > 
> > I personally don't like GLib that much, but I can give it a try and
> > prepare alternative patches with GLib.
> 
> On second glance I'm unclear the recommended way to deal with GLib
> DBus APIs when you have long running APIs to invoke. I get the feeling
> that you would end up using GTask to spawn single-use threads to execute
> the API call, but keeping all dbus interaction in the main thread.
> This would likely make it desirable to use the libvirt-gobject library
> at the same time to get easier access to async libvirt API execution.
> 
> I've copied you on a mail to the DBus mailing list to see if we can get
> some guidance from people with more up2date knowledge than me, since
> I've not been actively involved in dbus community for a little while now.

Unfortunately it appears you were not CC'd on the reply, but also,
for sake of archives, here is the thread:

  https://lists.freedesktop.org/archives/dbus/2018-March/017425.html

This comment is exactly the kind of thing I was concerned about:

 "fd.o#102839 was a bug in code that was already meant to be thread-safe
  since at least 2005, and was previously fixed in 2005, 2006, and twice
  in 2010. I think the fact that we are still finding bugs in the locking
  model in 2018 should tell you something!"


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list