diff --git a/meson.build b/meson.build
index 3d15b4ee34..b5b1223227 100644
--- a/meson.build
+++ b/meson.build
@@ -1066,7 +1066,11 @@ endif
glib_version = '2.48.0'
glib_dep = dependency('glib-2.0', version: '>=' + glib_version)
gobject_dep = dependency('gobject-2.0', version: '>=' + glib_version)
-gio_dep = dependency('gio-2.0', version: '>=' + glib_version)
+if host_machine.system() == 'windows'
+ gio_dep = dependency('gio-2.0', version: '>=' + glib_version)
+else
+ gio_dep = dependency('gio-unix-2.0', version: '>=' + glib_version)
+endif
glib_dep = declare_dependency(
dependencies: [ glib_dep, gobject_dep, gio_dep ],
)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 4ab8832b37..d87425a64c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -257,6 +257,7 @@
@SRCDIR@src/util/virfirewall.c
@SRCDIR@src/util/virfirewalld.c
@SRCDIR@src/util/virfirmware.c
+@SRCDIR@src/util/virgdbus.c
@SRCDIR@src/util/virhash.c
@SRCDIR@src/util/virhook.c
@SRCDIR@src/util/virhostcpu.c
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 5842b8d23d..fea5a49e55 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2190,6 +2190,20 @@ virFirmwareParse;
virFirmwareParseList;
+# util/virgdbus.h
+virGDBusCallMethod;
+virGDBusCallMethodWithFD;
+virGDBusCloseSystemBus;
+virGDBusErrorIsUnknownMethod;
+virGDBusGetSessionBus;
+virGDBusGetSystemBus;
+virGDBusHasSystemBus;
+virGDBusIsServiceEnabled;
+virGDBusIsServiceRegistered;
+virGDBusMessageIsSignal;
+virGDBusSetSharedBus;
+
+
# util/virgettext.h
virGettextInitialize;
diff --git a/src/util/meson.build b/src/util/meson.build
index bf556e7ae6..8a9dcac053 100644
--- a/src/util/meson.build
+++ b/src/util/meson.build
@@ -33,6 +33,7 @@ util_sources = [
'virfirewall.c',
'virfirewalld.c',
'virfirmware.c',
+ 'virgdbus.c',
'virgettext.c',
'virgic.c',
'virhash.c',
diff --git a/src/util/virgdbus.c b/src/util/virgdbus.c
new file mode 100644
index 0000000000..535b19f0a4
--- /dev/null
+++ b/src/util/virgdbus.c
@@ -0,0 +1,425 @@
+/*
+ * virgdbus.c: helper for using GDBus
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * .
+ *
+ */
+
+#include
+
+#include "virerror.h"
+#include "virlog.h"
+#include "virgdbus.h"
+#include "virthread.h"
+
+
+#define VIR_FROM_THIS VIR_FROM_DBUS
+
+VIR_LOG_INIT("util.dbus");
+
+
+static bool sharedBus = true;
+static GDBusConnection *systemBus;
+static GDBusConnection *sessionBus;
+static virOnceControl systemOnce = VIR_ONCE_CONTROL_INITIALIZER;
+static virOnceControl sessionOnce = VIR_ONCE_CONTROL_INITIALIZER;
+static GError *systemError;
+static GError *sessionError;
+
+
+void
+virGDBusSetSharedBus(bool shared)
+{
+ sharedBus = shared;
+}
+
+
+static GDBusConnection *
+virGDBusBusInit(GBusType type, GError **error)
+{
+ g_autofree char *address = NULL;
+
+ if (sharedBus) {
+ return g_bus_get_sync(type, NULL, error);
+ } else {
+ address = g_dbus_address_get_for_bus_sync(type, NULL, error);
+ if (error)
+ return NULL;
+ return g_dbus_connection_new_for_address_sync(address,
+ G_DBUS_CONNECTION_FLAGS_NONE,
+ NULL,
+ NULL,
+ error);
+ }
+}
+
+
+static void
+virGDBusSystemBusInit(void)
+{
+ systemBus = virGDBusBusInit(G_BUS_TYPE_SYSTEM, &systemError);
+}
+
+
+static GDBusConnection *
+virGDBusGetSystemBusInternal(void)
+{
+ if (virOnce(&systemOnce, virGDBusSystemBusInit) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to run one time GDBus initializer"));
+ return NULL;
+ }
+
+ return systemBus;
+}
+
+
+GDBusConnection *
+virGDBusGetSystemBus(void)
+{
+ GDBusConnection *bus = virGDBusGetSystemBusInternal();
+
+ if (!bus) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to get system bus connection: %s"),
+ systemError->message);
+ return NULL;
+ }
+
+ return bus;
+}
+
+
+static void
+virGDBusSessionBusInit(void)
+{
+ sessionBus = virGDBusBusInit(G_BUS_TYPE_SESSION, &sessionError);
+}
+
+
+GDBusConnection *
+virGDBusGetSessionBus(void)
+{
+ if (virOnce(&sessionOnce, virGDBusSessionBusInit) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to run one time GDBus initializer"));
+ return NULL;
+ }
+
+ if (!sessionBus) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to get session bus connection: %s"),
+ sessionError->message);
+ return NULL;
+ }
+
+ return sessionBus;
+}
+
+
+/**
+ * virGDBusHasSystemBus:
+ *
+ * Check if DBus system bus is running. This does not imply that we have
+ * a connection. DBus might be running and refusing connections due to its
+ * client limit. The latter must be treated as a fatal error.
+ *
+ * Return false if dbus is not available, true if probably available.
+ */
+bool
+virGDBusHasSystemBus(void)
+{
+ g_autofree char *name = NULL;
+
+ if (virGDBusGetSystemBusInternal())
+ return true;
+
+ if (!g_dbus_error_is_remote_error(systemError))
+ return false;
+
+ name = g_dbus_error_get_remote_error(systemError);
+
+ if (name &&
+ (STREQ(name, "org.freedesktop.DBus.Error.FileNotFound") ||
+ STREQ(name, "org.freedesktop.DBus.Error.NoServer"))) {
+ VIR_DEBUG("System bus not available: %s", NULLSTR(systemError->message));
+ return false;
+ }
+
+ return true;
+}
+
+
+void
+virGDBusCloseSystemBus(void)
+{
+ if (!systemBus || sharedBus)
+ return;
+
+ g_dbus_connection_flush_sync(systemBus, NULL, NULL);
+ g_dbus_connection_close_sync(systemBus, NULL, NULL);
+ g_object_unref(systemBus);
+ systemBus = NULL;
+}
+
+
+#define VIR_DBUS_METHOD_CALL_TIMEOUT_MILIS 30 * 1000
+
+/**
+ * virGDBusCallMethod:
+ * @conn: a DBus connection
+ * @reply: pointer to receive reply message, or NULL
+ * @error: libvirt error pointer or NULL
+ * @busName: bus identifier of the target service
+ * @objectPath: object path of the target service
+ * @ifaceName: the interface of the object
+ * @method: the name of the method in the interface
+ * @data: pointer to data passed to DBus method
+ *
+ * If @error is NULL then a libvirt error will be raised when a DBus error
+ * is received and the return value will be -1. If @error is non-NULL then
+ * any DBus error will be saved into that object and the return value will
+ * be 0.
+ *
+ * Returns 0 on success, or -1 upon error.
+ */
+int
+virGDBusCallMethod(GDBusConnection *conn,
+ GVariant **reply,
+ virErrorPtr error,
+ const char *busName,
+ const char *objectPath,
+ const char *ifaceName,
+ const char *method,
+ GVariant *data)
+{
+ g_autoptr(GVariant) ret = NULL;
+ g_autoptr(GError) gerror = NULL;
+
+ if (error)
+ memset(error, 0, sizeof(*error));
+
+ if (data)
+ g_variant_ref_sink(data);
+
+ ret = g_dbus_connection_call_sync(conn,
+ busName,
+ objectPath,
+ ifaceName,
+ method,
+ data,
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ VIR_DBUS_METHOD_CALL_TIMEOUT_MILIS,
+ NULL,
+ &gerror);
+
+ if (!ret) {
+ if (error && g_dbus_error_is_remote_error(gerror)) {
+ error->level = VIR_ERR_ERROR;
+ error->code = VIR_ERR_DBUS_SERVICE;
+ error->domain = VIR_FROM_DBUS;
+ error->str1 = g_dbus_error_get_remote_error(gerror);
+ error->message = g_strdup(gerror->message);
+ } else {
+ virReportError(VIR_ERR_DBUS_SERVICE, "%s", gerror->message);
+ return -1;
+ }
+ }
+
+ if (reply)
+ *reply = g_steal_pointer(&ret);
+
+ return 0;
+}
+
+
+#ifdef G_OS_UNIX
+int
+virGDBusCallMethodWithFD(GDBusConnection *conn,
+ GVariant **reply,
+ GUnixFDList **replyFD,
+ virErrorPtr error,
+ const char *busName,
+ const char *objectPath,
+ const char *ifaceName,
+ const char *method,
+ GVariant *data,
+ GUnixFDList *dataFD)
+{
+ g_autoptr(GVariant) ret = NULL;
+ g_autoptr(GError) gerror = NULL;
+
+ if (error)
+ memset(error, 0, sizeof(*error));
+
+ if (data)
+ g_variant_ref_sink(data);
+
+ ret = g_dbus_connection_call_with_unix_fd_list_sync(conn,
+ busName,
+ objectPath,
+ ifaceName,
+ method,
+ data,
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ VIR_DBUS_METHOD_CALL_TIMEOUT_MILIS,
+ dataFD,
+ replyFD,
+ NULL,
+ &gerror);
+
+ if (!ret) {
+ if (error && g_dbus_error_is_remote_error(gerror)) {
+ error->level = VIR_ERR_ERROR;
+ error->code = VIR_ERR_DBUS_SERVICE;
+ error->domain = VIR_FROM_DBUS;
+ error->str1 = g_dbus_error_get_remote_error(gerror);
+ error->message = g_strdup(gerror->message);
+
+ if (!error->str1 || !error->message)
+ return -1;
+ } else {
+ virReportError(VIR_ERR_DBUS_SERVICE, "%s", gerror->message);
+ return -1;
+ }
+ }
+
+ if (reply)
+ *reply = g_steal_pointer(&ret);
+
+ return 0;
+}
+#else
+int
+virGDBusCallMethodWithFD(GDBusConnection *conn G_GNUC_UNUSED,
+ GVariant **reply G_GNUC_UNUSED,
+ GUnixFDList **replyFD G_GNUC_UNUSED,
+ virErrorPtr error G_GNUC_UNUSED,
+ const char *busName G_GNUC_UNUSED,
+ const char *objectPath G_GNUC_UNUSED,
+ const char *ifaceName G_GNUC_UNUSED,
+ const char *method G_GNUC_UNUSED,
+ GVariant *data G_GNUC_UNUSED,
+ GUnixFDList *dataFD G_GNUC_UNUSED)
+{
+ virReportSystemError(ENOSYS, "%s",
+ _("Unix file descriptors not supported on this platform"));
+ return -1;
+}
+#endif
+
+
+static int
+virGDBusIsServiceInList(const char *listMethod,
+ const char *name)
+{
+ GDBusConnection *conn;
+ g_autoptr(GVariant) reply = NULL;
+ g_autoptr(GVariantIter) iter = NULL;
+ char *str;
+ int rc;
+
+ if (!virGDBusHasSystemBus())
+ return -2;
+
+ conn = virGDBusGetSystemBus();
+ if (!conn)
+ return -1;
+
+ rc = virGDBusCallMethod(conn,
+ &reply,
+ NULL,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ listMethod,
+ NULL);
+
+ if (rc < 0)
+ return -1;
+
+ g_variant_get(reply, "(as)", &iter);
+ while (g_variant_iter_loop(iter, "s", &str)) {
+ if (STREQ(str, name))
+ return 0;
+ }
+
+ return -2;
+}
+
+
+/**
+ * virGDBusIsServiceEnabled:
+ * @name: service name
+ *
+ * Returns 0 if service is available, -1 on fatal error, or -2 if service is not available
+ */
+int
+virGDBusIsServiceEnabled(const char *name)
+{
+ int ret = virGDBusIsServiceInList("ListActivatableNames", name);
+
+ VIR_DEBUG("Service %s is %s", name, ret ? "unavailable" : "available");
+
+ return ret;
+}
+
+
+/**
+ * virGDBusIsServiceRegistered:
+ * @name: service name
+ *
+ * Returns 0 if service is registered, -1 on fatal error, or -2 if service is not registered
+ */
+int
+virGDBusIsServiceRegistered(const char *name)
+{
+ int ret = virGDBusIsServiceInList("ListNames", name);
+
+ VIR_DEBUG("Service %s is %s", name, ret ? "not registered" : "registered");
+
+ return ret;
+}
+
+
+bool
+virGDBusErrorIsUnknownMethod(virErrorPtr err)
+{
+ return err->domain == VIR_FROM_DBUS &&
+ err->code == VIR_ERR_DBUS_SERVICE &&
+ err->level == VIR_ERR_ERROR &&
+ STREQ_NULLABLE("org.freedesktop.DBus.Error.UnknownMethod",
+ err->str1);
+}
+
+
+bool
+virGDBusMessageIsSignal(GDBusMessage *message,
+ const char *iface,
+ const char *signal)
+{
+ GDBusMessageType type = g_dbus_message_get_message_type(message);
+
+ if (type == G_DBUS_MESSAGE_TYPE_SIGNAL) {
+ const char *interface = g_dbus_message_get_interface(message);
+ const char *member = g_dbus_message_get_member(message);
+
+ return STREQ(interface, iface) && STREQ(member, signal);
+ }
+
+ return false;
+}
diff --git a/src/util/virgdbus.h b/src/util/virgdbus.h
new file mode 100644
index 0000000000..6ea717eea2
--- /dev/null
+++ b/src/util/virgdbus.h
@@ -0,0 +1,79 @@
+/*
+ * virgdbus.h: helper for using GDBus
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * .
+ *
+ */
+
+#pragma once
+
+#include
+
+#ifdef G_OS_UNIX
+# include
+#endif
+
+#include "internal.h"
+
+void
+virGDBusSetSharedBus(bool shared);
+
+GDBusConnection *
+virGDBusGetSystemBus(void);
+
+GDBusConnection *
+virGDBusGetSessionBus(void);
+
+bool
+virGDBusHasSystemBus(void);
+
+void
+virGDBusCloseSystemBus(void);
+
+int
+virGDBusCallMethod(GDBusConnection *conn,
+ GVariant **reply,
+ virErrorPtr error,
+ const char *busName,
+ const char *objectPath,
+ const char *ifaceName,
+ const char *method,
+ GVariant *data);
+
+int
+virGDBusCallMethodWithFD(GDBusConnection *conn,
+ GVariant **reply,
+ GUnixFDList **replyFD,
+ virErrorPtr error,
+ const char *busName,
+ const char *objectPath,
+ const char *ifaceName,
+ const char *method,
+ GVariant *data,
+ GUnixFDList *dataFD);
+
+int
+virGDBusIsServiceEnabled(const char *name);
+
+int
+virGDBusIsServiceRegistered(const char *name);
+
+bool
+virGDBusErrorIsUnknownMethod(virErrorPtr err);
+
+bool
+virGDBusMessageIsSignal(GDBusMessage *message,
+ const char *iface,
+ const char *signal);