[libvirt/f18] Cleanly save session VMs on logout/shutdown (bz #872254)
Cole Robinson
crobinso at fedoraproject.org
Tue Nov 13 13:54:01 UTC 2012
commit cc9cfaa90dea7d34b5ed1512dc216d295df45566
Author: Cole Robinson <crobinso at redhat.com>
Date: Tue Nov 13 08:53:57 2012 -0500
Cleanly save session VMs on logout/shutdown (bz #872254)
libvirt-dbus.patch | 225 +++++++++++++++++++++++++++++
libvirt-save-with-session.patch | 303 +++++++++++++++++++++++++++++++++++++++
libvirt.spec | 11 ++-
3 files changed, 538 insertions(+), 1 deletions(-)
---
diff --git a/libvirt-dbus.patch b/libvirt-dbus.patch
new file mode 100644
index 0000000..eb3e2c6
--- /dev/null
+++ b/libvirt-dbus.patch
@@ -0,0 +1,225 @@
+Return-Path: alexl at redhat.com
+Received: from zmta04.collab.prod.int.phx2.redhat.com (LHLO
+ zmta04.collab.prod.int.phx2.redhat.com) (10.5.81.11) by
+ zmail20.collab.prod.int.phx2.redhat.com with LMTP; Tue, 9 Oct 2012 11:26:38
+ -0400 (EDT)
+Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23])
+ by zmta04.collab.prod.int.phx2.redhat.com (Postfix) with ESMTP id D4096D0927
+ for <alexl at mail.corp.redhat.com>; Tue, 9 Oct 2012 11:26:38 -0400 (EDT)
+Received: from localhost.localdomain (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1])
+ by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q99FQV93016417;
+ Tue, 9 Oct 2012 11:26:33 -0400
+From: Alexander Larsson <alexl at redhat.com>
+To: libvir-list at redhat.com
+Cc: Alexander Larsson <alexl at redhat.com>
+Subject: [PATCH 1/2] virdbus: Add virDBusGetSessionBus helper
+Date: Tue, 9 Oct 2012 17:26:28 +0200
+Message-Id: <1349796389-6122-2-git-send-email-alexl at redhat.com>
+In-Reply-To: <1349796389-6122-1-git-send-email-alexl at redhat.com>
+References: <1349796389-6122-1-git-send-email-alexl at redhat.com>
+X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23
+
+This splits out some common code from virDBusGetSystemBus and
+uses it to implement a new virDBusGetSessionBus helper.
+---
+ src/libvirt_private.syms | 1 +
+ src/util/virdbus.c | 84 ++++++++++++++++++++++++++++++++++++------------
+ src/util/virdbus.h | 1 +
+ 3 files changed, 66 insertions(+), 20 deletions(-)
+
+diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
+index a8c81e7..88f1b2f 100644
+--- a/src/libvirt_private.syms
++++ b/src/libvirt_private.syms
+@@ -1310,6 +1310,7 @@ virConsoleOpen;
+
+ # virdbus.h
+ virDBusGetSystemBus;
++virDBusGetSessionBus;
+
+
+ # virdomainlist.h
+diff --git a/src/util/virdbus.c b/src/util/virdbus.c
+index 4acce12..2dc7265 100644
+--- a/src/util/virdbus.c
++++ b/src/util/virdbus.c
+@@ -32,40 +32,49 @@
+ #ifdef HAVE_DBUS
+
+ static DBusConnection *systembus = NULL;
+-static virOnceControl once = VIR_ONCE_CONTROL_INITIALIZER;
+-static DBusError dbuserr;
++static DBusConnection *sessionbus = NULL;
++static virOnceControl systemonce = VIR_ONCE_CONTROL_INITIALIZER;
++static virOnceControl sessiononce = VIR_ONCE_CONTROL_INITIALIZER;
++static DBusError systemdbuserr;
++static DBusError sessiondbuserr;
+
+ static dbus_bool_t virDBusAddWatch(DBusWatch *watch, void *data);
+ static void virDBusRemoveWatch(DBusWatch *watch, void *data);
+ static void virDBusToggleWatch(DBusWatch *watch, void *data);
+
+-static void virDBusSystemBusInit(void)
++static DBusConnection *virDBusBusInit(DBusBusType type, DBusError *dbuserr)
+ {
++ DBusConnection *bus;
++
+ /* Allocate and initialize a new HAL context */
+ dbus_connection_set_change_sigpipe(FALSE);
+ dbus_threads_init_default();
+
+- dbus_error_init(&dbuserr);
+- if (!(systembus = dbus_bus_get(DBUS_BUS_SYSTEM, &dbuserr)))
+- return;
++ dbus_error_init(dbuserr);
++ if (!(bus = dbus_bus_get(type, dbuserr)))
++ return NULL;
+
+- dbus_connection_set_exit_on_disconnect(systembus, FALSE);
++ dbus_connection_set_exit_on_disconnect(bus, FALSE);
+
+ /* Register dbus watch callbacks */
+- if (!dbus_connection_set_watch_functions(systembus,
++ if (!dbus_connection_set_watch_functions(bus,
+ virDBusAddWatch,
+ virDBusRemoveWatch,
+ virDBusToggleWatch,
+- NULL, NULL)) {
+- systembus = NULL;
+- return;
++ bus, NULL)) {
++ return NULL;
+ }
++ return bus;
+ }
+
++static void virDBusSystemBusInit(void)
++{
++ systembus = virDBusBusInit (DBUS_BUS_SYSTEM, &systemdbuserr);
++}
+
+ DBusConnection *virDBusGetSystemBus(void)
+ {
+- if (virOnce(&once, virDBusSystemBusInit) < 0) {
++ if (virOnce(&systemonce, virDBusSystemBusInit) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to run one time DBus initializer"));
+ return NULL;
+@@ -74,7 +83,7 @@ DBusConnection *virDBusGetSystemBus(void)
+ if (!systembus) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to get DBus system bus connection: %s"),
+- dbuserr.message ? dbuserr.message : "watch setup failed");
++ systemdbuserr.message ? systemdbuserr.message : "watch setup failed");
+ return NULL;
+ }
+
+@@ -82,13 +91,45 @@ DBusConnection *virDBusGetSystemBus(void)
+ }
+
+
++static void virDBusSessionBusInit(void)
++{
++ sessionbus = virDBusBusInit (DBUS_BUS_SESSION, &sessiondbuserr);
++}
++
++DBusConnection *virDBusGetSessionBus(void)
++{
++ if (virOnce(&sessiononce, virDBusSessionBusInit) < 0) {
++ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
++ _("Unable to run one time DBus initializer"));
++ return NULL;
++ }
++
++ if (!sessionbus) {
++ virReportError(VIR_ERR_INTERNAL_ERROR,
++ _("Unable to get DBus session bus connection: %s"),
++ sessiondbuserr.message ? sessiondbuserr.message : "watch setup failed");
++ return NULL;
++ }
++
++ return sessionbus;
++}
++
++struct virDBusWatch
++{
++ int watch;
++ DBusConnection *bus;
++};
++
+ static void virDBusWatchCallback(int fdatch ATTRIBUTE_UNUSED,
+ int fd ATTRIBUTE_UNUSED,
+ int events, void *opaque)
+ {
+ DBusWatch *watch = opaque;
++ struct virDBusWatch *info;
+ int dbus_flags = 0;
+
++ info = dbus_watch_get_data(watch);
++
+ if (events & VIR_EVENT_HANDLE_READABLE)
+ dbus_flags |= DBUS_WATCH_READABLE;
+ if (events & VIR_EVENT_HANDLE_WRITABLE)
+@@ -100,7 +141,7 @@ static void virDBusWatchCallback(int fdatch ATTRIBUTE_UNUSED,
+
+ (void)dbus_watch_handle(watch, dbus_flags);
+
+- while (dbus_connection_dispatch(systembus) == DBUS_DISPATCH_DATA_REMAINS)
++ while (dbus_connection_dispatch(info->bus) == DBUS_DISPATCH_DATA_REMAINS)
+ /* keep dispatching while data remains */;
+ }
+
+@@ -120,18 +161,13 @@ static int virDBusTranslateWatchFlags(int dbus_flags)
+ }
+
+
+-struct virDBusWatch
+-{
+- int watch;
+-};
+-
+ static void virDBusWatchFree(void *data) {
+ struct virDBusWatch *info = data;
+ VIR_FREE(info);
+ }
+
+ static dbus_bool_t virDBusAddWatch(DBusWatch *watch,
+- void *data ATTRIBUTE_UNUSED)
++ void *data)
+ {
+ int flags = 0;
+ int fd;
+@@ -148,6 +184,7 @@ static dbus_bool_t virDBusAddWatch(DBusWatch *watch,
+ # else
+ fd = dbus_watch_get_fd(watch);
+ # endif
++ info->bus = (DBusConnection *)data;
+ info->watch = virEventAddHandle(fd, flags,
+ virDBusWatchCallback,
+ watch, NULL);
+@@ -194,4 +231,11 @@ DBusConnection *virDBusGetSystemBus(void)
+ return NULL;
+ }
+
++DBusConnection *virDBusGetSessionBus(void)
++{
++ virReportError(VIR_ERR_INTERNAL_ERROR,
++ "%s", _("DBus support not compiled into this binary"));
++ return NULL;
++}
++
+ #endif /* ! HAVE_DBUS */
+diff --git a/src/util/virdbus.h b/src/util/virdbus.h
+index 27dca00..e443fbe 100644
+--- a/src/util/virdbus.h
++++ b/src/util/virdbus.h
+@@ -30,5 +30,6 @@
+ # include "internal.h"
+
+ DBusConnection *virDBusGetSystemBus(void);
++DBusConnection *virDBusGetSessionBus(void);
+
+ #endif /* __VIR_DBUS_H__ */
+--
+1.7.12.1
+
diff --git a/libvirt-save-with-session.patch b/libvirt-save-with-session.patch
new file mode 100644
index 0000000..2cc8dc6
--- /dev/null
+++ b/libvirt-save-with-session.patch
@@ -0,0 +1,303 @@
+Return-Path: alexl at redhat.com
+Received: from zmta06.collab.prod.int.phx2.redhat.com (LHLO
+ zmta06.collab.prod.int.phx2.redhat.com) (10.5.81.13) by
+ zmail20.collab.prod.int.phx2.redhat.com with LMTP; Tue, 9 Oct 2012 11:26:39
+ -0400 (EDT)
+Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23])
+ by zmta06.collab.prod.int.phx2.redhat.com (Postfix) with ESMTP id D4A8516044F
+ for <alexl at mail.corp.redhat.com>; Tue, 9 Oct 2012 11:26:39 -0400 (EDT)
+Received: from localhost.localdomain (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1])
+ by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q99FQV94016417;
+ Tue, 9 Oct 2012 11:26:34 -0400
+From: Alexander Larsson <alexl at redhat.com>
+To: libvir-list at redhat.com
+Cc: Alexander Larsson <alexl at redhat.com>
+Subject: [PATCH 2/2] Shut down session libvirtd cleanly
+Date: Tue, 9 Oct 2012 17:26:29 +0200
+Message-Id: <1349796389-6122-3-git-send-email-alexl at redhat.com>
+In-Reply-To: <1349796389-6122-1-git-send-email-alexl at redhat.com>
+References: <1349796389-6122-1-git-send-email-alexl at redhat.com>
+X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23
+
+When the session dies or when the system is going to be shut down
+we save all active VMs and exit libvirtd.
+
+Additionally whenever there is an active domain we hold a
+shutdown inhibitor to avoid shutting down before all the
+VMs are saved.
+---
+ daemon/libvirtd.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 244 insertions(+)
+
+diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c
+index b49acc5..c3bf2ce 100644
+--- a/daemon/libvirtd.c
++++ b/daemon/libvirtd.c
+@@ -98,6 +98,11 @@
+
+ #include "configmake.h"
+
++#ifdef HAVE_DBUS
++# include <dbus/dbus.h>
++# include "virdbus.h"
++#endif
++
+ #if HAVE_SASL
+ virNetSASLContextPtr saslCtxt = NULL;
+ #endif
+@@ -769,6 +774,212 @@ static int daemonSetupSignals(virNetServerPtr srv)
+ return 0;
+ }
+
++#ifdef HAVE_DBUS
++
++static DBusConnection *sessionBus;
++static DBusConnection *systemBus;
++static virConnectPtr sessionConnection;
++static int numActiveDomains;
++static bool hasInhibit;
++static bool callingInhibit;
++static int inhibitFd = -1;
++
++static void runSaveAllDomains(void *opaque)
++{
++ virNetServerPtr srv = opaque;
++ int numDomains, i;
++ int state;
++ virDomainPtr *domains = NULL;
++ unsigned int *flags = NULL;
++
++ numDomains = virConnectListAllDomains(sessionConnection, &domains, VIR_CONNECT_LIST_DOMAINS_ACTIVE);
++ if (numDomains < 0)
++ goto cleanup;
++
++ if (VIR_ALLOC_N(flags, numDomains) < 0) {
++ virReportOOMError();
++ goto cleanup;
++ }
++
++ /* First we pause all VMs to make them stop dirtying
++ pages, etc. We remember if any VMs were paused so
++ we can restore that on resume. */
++ for (i = 0 ; i < numDomains ; i++) {
++ flags[i] = VIR_DOMAIN_SAVE_RUNNING;
++ if (virDomainGetState (domains[i], &state, NULL, 0) == 0) {
++ if (state == VIR_DOMAIN_PAUSED) {
++ flags[i] = VIR_DOMAIN_SAVE_PAUSED;
++ }
++ }
++ virDomainSuspend (domains[i]);
++ }
++
++ /* Then we save the VMs to disk */
++ for (i = 0 ; i < numDomains ; i++)
++ virDomainManagedSave (domains[i], flags[i]);
++
++ VIR_FREE (domains);
++ VIR_FREE (flags);
++
++ cleanup:
++ if (domains != NULL) {
++ for (i = 0 ; i < numDomains ; i++)
++ virDomainFree (domains[i]);
++ VIR_FREE (domains);
++ }
++ if (flags != NULL)
++ VIR_FREE (flags);
++
++ /* We don't need any shutdown inhibit lock anymore now */
++ if (inhibitFd != -1) {
++ if (VIR_CLOSE (inhibitFd) < 0)
++ virReportSystemError(errno, "%s", _("failed to close file"));
++ inhibitFd = -1;
++ }
++
++ /* Exit libvirtd cleanly */
++ virNetServerQuit (srv);
++}
++
++/* We do this in a thread to not block the main loop */
++static void saveAllDomains(virNetServerPtr srv)
++{
++ virThread thr;
++ virObjectRef(srv);
++ if (virThreadCreate(&thr, false, runSaveAllDomains, srv) < 0) {
++ virObjectUnref(srv);
++ }
++}
++
++static void gotInhibitReply (DBusPendingCall *pending,
++ void *opaque ATTRIBUTE_UNUSED)
++{
++ DBusMessage *reply;
++ int fd;
++
++ callingInhibit = false;
++
++ reply = dbus_pending_call_steal_reply (pending);
++ if (reply == NULL)
++ return;
++
++ if (dbus_message_get_args (reply, NULL,
++ DBUS_TYPE_UNIX_FD, &fd,
++ DBUS_TYPE_INVALID)) {
++ if (hasInhibit)
++ inhibitFd = fd;
++ else {
++ /* We stopped the last VM since we made the inhibit call */
++ if (VIR_CLOSE (fd) < 0) {
++ virReportSystemError(errno, "%s", _("failed to close file"));
++ }
++ }
++ }
++ dbus_message_unref (reply);
++}
++
++/* As per: http://www.freedesktop.org/wiki/Software/systemd/inhibit */
++static void callInhibit(const char *what,
++ const char *who,
++ const char *why,
++ const char *mode)
++{
++ DBusMessage *message;
++ DBusPendingCall *pendingReply;
++
++ if (systemBus == NULL)
++ return;
++
++ /* Only one outstanding call at a time */
++ if (callingInhibit)
++ return;
++
++ message = dbus_message_new_method_call ("org.freedesktop.login1",
++ "/org/freedesktop/login1",
++ "org.freedesktop.login1.Manager",
++ "Inhibit");
++ if (message == NULL)
++ return;
++
++ dbus_message_append_args (message,
++ DBUS_TYPE_STRING, &what,
++ DBUS_TYPE_STRING, &who,
++ DBUS_TYPE_STRING, &why,
++ DBUS_TYPE_STRING, &mode,
++ DBUS_TYPE_INVALID);
++
++ pendingReply = NULL;
++ if (dbus_connection_send_with_reply (systemBus, message,
++ &pendingReply,
++ 25*1000)) {
++ dbus_pending_call_set_notify (pendingReply,
++ gotInhibitReply,
++ NULL, NULL);
++ callingInhibit = true;
++ }
++ dbus_message_unref (message);
++}
++
++
++static void numActiveDomainsChanged(void)
++{
++ if (numActiveDomains > 0 && !hasInhibit) {
++ callInhibit("shutdown", _("Libvirt"), _("Virtual machines need to be saved"), "delay");
++ hasInhibit = true;
++ } else if (numActiveDomains == 0 && hasInhibit) {
++ if (inhibitFd != -1) {
++ if (VIR_CLOSE (inhibitFd) < 0) {
++ virReportSystemError(errno, "%s", _("failed to close file"));
++ }
++ inhibitFd = -1;
++ }
++ hasInhibit = false;
++ }
++}
++
++static int lifecycleEventCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
++ virDomainPtr dom ATTRIBUTE_UNUSED,
++ int event,
++ int detail ATTRIBUTE_UNUSED,
++ void *opaque ATTRIBUTE_UNUSED)
++{
++ if (event == VIR_DOMAIN_EVENT_STOPPED)
++ numActiveDomains--;
++ else if (event == VIR_DOMAIN_EVENT_STARTED)
++ numActiveDomains++;
++
++ numActiveDomainsChanged();
++
++ return 0;
++}
++
++static DBusHandlerResult handleSessionMessageFunc(DBusConnection *connection ATTRIBUTE_UNUSED,
++ DBusMessage *message,
++ void *userData)
++{
++ virNetServerPtr srv = userData;
++
++ if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
++ saveAllDomains (srv);
++ }
++
++ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
++}
++
++static DBusHandlerResult handleSystemMessageFunc(DBusConnection *connection ATTRIBUTE_UNUSED,
++ DBusMessage *message,
++ void *userData)
++{
++ virNetServerPtr srv = userData;
++
++ if (dbus_message_is_signal(message, "org.freedesktop.login1.Manager", "PrepareForShutdown")) {
++ saveAllDomains (srv);
++ }
++
++ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
++}
++#endif
++
+ static void daemonRunStateInit(void *opaque)
+ {
+ virNetServerPtr srv = opaque;
+@@ -785,6 +996,39 @@ static void daemonRunStateInit(void *opaque)
+ return;
+ }
+
++#ifdef HAVE_DBUS
++ /* Tie the non-priviledged libvirtd to the session/shutdown lifecycle */
++ if (!virNetServerIsPrivileged(srv)) {
++
++ sessionBus = virDBusGetSessionBus ();
++ if (sessionBus != NULL) {
++ dbus_connection_add_filter(sessionBus,
++ handleSessionMessageFunc, srv, NULL);
++ }
++
++ systemBus = virDBusGetSystemBus ();
++ if (systemBus != NULL) {
++ dbus_connection_add_filter(systemBus,
++ handleSystemMessageFunc, srv, NULL);
++ dbus_bus_add_match(systemBus,
++ "type='signal',sender='org.freedesktop.login1', interface='org.freedesktop.login1.Manager'",
++ NULL);
++ }
++
++ sessionConnection = virConnectOpen("qemu:///session");
++ if (sessionConnection != NULL) {
++ numActiveDomains = virConnectNumOfDomains(sessionConnection);
++ virConnectDomainEventRegisterAny(sessionConnection,
++ NULL,
++ VIR_DOMAIN_EVENT_ID_LIFECYCLE,
++ VIR_DOMAIN_EVENT_CALLBACK (lifecycleEventCallback),
++ NULL, NULL);
++ numActiveDomainsChanged();
++ }
++
++ }
++#endif
++
+ /* Only now accept clients from network */
+ virNetServerUpdateServices(srv, true);
+ virObjectUnref(srv);
+--
+1.7.12.1
+
diff --git a/libvirt.spec b/libvirt.spec
index e419deb..5547182 100644
--- a/libvirt.spec
+++ b/libvirt.spec
@@ -320,7 +320,7 @@
Summary: Library providing a simple virtualization API
Name: libvirt
Version: 0.10.2.1
-Release: 2%{?dist}%{?extra_release}
+Release: 3%{?dist}%{?extra_release}
License: LGPLv2+
Group: Development/Libraries
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
@@ -333,6 +333,10 @@ Source: http://libvirt.org/sources/%{?mainturl}libvirt-%{version}.tar.gz
# Fix qemu -> qemu-system-i386 (RHBZ#857026).
# keep: This patch is Fedora-specific and not upstream.
Patch1: 0001-Use-qemu-system-i386-as-binary-instead-of-qemu.patch
+# Cleanly save session VMs on logout/shutdown (bz 872254)
+# keep: Fixed upstream, but using patches not suitable for stable
+Patch2: libvirt-dbus.patch
+Patch3: libvirt-save-with-session.patch
@@ -1051,6 +1055,8 @@ of recent versions of Linux (and other OSes).
%prep
%setup -q
%patch1 -p1
+%patch2 -p1
+%patch3 -p1
%build
%if ! %{with_xen}
@@ -1903,6 +1909,9 @@ rm -f $RPM_BUILD_ROOT%{_sysconfdir}/sysctl.d/libvirtd
%endif
%changelog
+* Tue Nov 13 2012 Cole Robinson <crobinso at redhat.com> - 0.10.2.1-3
+- Cleanly save session VMs on logout/shutdown (bz #872254)
+
* Tue Oct 30 2012 Cole Robinson <crobinso at redhat.com> - 0.10.2.1-2
- Disable libxl on F18 too
More information about the scm-commits
mailing list