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(a)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