[libvirt] [PATCH 0/2] Cleanly save session VMs on logout/shutdown

Atm, when you exit the sessin (via logout or shutdown for instance) we just leave the qemu:///session handling libvirtd process around, including any running VMs. This is not particularly nice, as there is no way to know that the VMs are running, and you risk data loss if you shutdown while the VMs are running. This new version is basically the same as the previous, except it runs inside libvirtd, and it exits libvirtd too after saving the VMs. Alexander Larsson (2): virdbus: Add virDBusGetSessionBus helper Shut down session libvirtd cleanly daemon/libvirtd.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++ src/libvirt_private.syms | 1 + src/util/virdbus.c | 84 ++++++++++++---- src/util/virdbus.h | 1 + 4 files changed, 310 insertions(+), 20 deletions(-) -- 1.7.12.1

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

On Tue, Oct 09, 2012 at 05:26:28PM +0200, Alexander Larsson wrote:
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(-)
ACK Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

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

On Tue, Oct 09, 2012 at 05:26:29PM +0200, Alexander Larsson wrote:
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 +
One style issue throughout this is that we prefer to avoid having any whitespace between the function name and the opening '(' from the parameters. Having thought about this some more, I see one design issue, namely that we're hardcoding usage of qemu in the main part of libvirtd. This will fail in cases where the QEMU driver is compiled out of libvirtd, and also ignores similar needs for the UML driver. I think what we need todo is to introduce a new internal API method virStateSave() which does the actual work of saving the guests, and then simply call that from the central daemon code. Rather than force you to learn all about this part of libvirt internals, I'll adjust your current patch to work this way and re-post it myself for review & testing. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (2)
-
Alexander Larsson
-
Daniel P. Berrange