[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