From cc9cfaa90dea7d34b5ed1512dc216d295df45566 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 13 Nov 2012 08:53:57 -0500 Subject: [PATCH] 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 deletion(-) create mode 100644 libvirt-dbus.patch create mode 100644 libvirt-save-with-session.patch 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@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 ; 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 +To: libvir-list@redhat.com +Cc: Alexander Larsson +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@redhat.com> +In-Reply-To: <1349796389-6122-1-git-send-email-alexl@redhat.com> +References: <1349796389-6122-1-git-send-email-alexl@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@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 ; 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 +To: libvir-list@redhat.com +Cc: Alexander Larsson +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@redhat.com> +In-Reply-To: <1349796389-6122-1-git-send-email-alexl@redhat.com> +References: <1349796389-6122-1-git-send-email-alexl@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 ++# 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 - 0.10.2.1-3 +- Cleanly save session VMs on logout/shutdown (bz #872254) + * Tue Oct 30 2012 Cole Robinson - 0.10.2.1-2 - Disable libxl on F18 too