[libvirt] [PATCH 09/11] Inhibit desktop shutdown while any virtual machines are running

Daniel P. Berrange berrange at redhat.com
Wed Oct 31 19:03:56 UTC 2012


From: "Daniel P. Berrange" <berrange at redhat.com>

Use the freedesktop inhibition DBus service to prevent host
shutdown or session logout while any VMs are running.

Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
---
 src/rpc/virnetserver.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)

diff --git a/src/rpc/virnetserver.c b/src/rpc/virnetserver.c
index b108399..aaf98a9 100644
--- a/src/rpc/virnetserver.c
+++ b/src/rpc/virnetserver.c
@@ -27,6 +27,10 @@
 #include <string.h>
 #include <fcntl.h>
 
+#ifdef HAVE_DBUS
+# include <dbus/dbus.h>
+#endif
+
 #include "virnetserver.h"
 #include "logging.h"
 #include "memory.h"
@@ -37,6 +41,7 @@
 #include "virfile.h"
 #include "event.h"
 #include "virnetservermdns.h"
+#include "virdbus.h"
 
 #ifndef SA_SIGINFO
 # define SA_SIGINFO 0
@@ -102,6 +107,8 @@ struct _virNetServer {
 
     unsigned int autoShutdownTimeout;
     size_t autoShutdownInhibitions;
+    bool autoShutdownCallingInhibit;
+    int autoShutdownInhibitFd;
 
     virNetServerClientPrivNew clientPrivNew;
     virNetServerClientPrivPreExecRestart clientPrivPreExecRestart;
@@ -391,6 +398,7 @@ virNetServerPtr virNetServerNew(size_t min_workers,
     srv->clientPrivFree = clientPrivFree;
     srv->clientPrivOpaque = clientPrivOpaque;
     srv->privileged = geteuid() == 0 ? true : false;
+    srv->autoShutdownInhibitFd = -1;
 
     if (mdnsGroupName &&
         !(srv->mdnsGroupName = strdup(mdnsGroupName))) {
@@ -716,10 +724,104 @@ void virNetServerAutoShutdown(virNetServerPtr srv,
 }
 
 
+#ifdef HAVE_DBUS
+static void virNetServerGotInhibitReply(DBusPendingCall *pending,
+                                        void *opaque)
+{
+    virNetServerPtr srv = opaque;
+    DBusMessage *reply;
+    int fd;
+
+    virNetServerLock(srv);
+    srv->autoShutdownCallingInhibit = false;
+
+    VIR_DEBUG("srv=%p", srv);
+
+    reply = dbus_pending_call_steal_reply(pending);
+    if (reply == NULL)
+        goto cleanup;
+
+    if (dbus_message_get_args(reply, NULL,
+                              DBUS_TYPE_UNIX_FD, &fd,
+                              DBUS_TYPE_INVALID)) {
+        if (srv->autoShutdownInhibitions) {
+            srv->autoShutdownInhibitFd = fd;
+        } else {
+            /* We stopped the last VM since we made the inhibit call */
+            VIR_FORCE_CLOSE(fd);
+        }
+    }
+    dbus_message_unref(reply);
+
+cleanup:
+    virNetServerUnlock(srv);
+}
+
+
+/* As per: http://www.freedesktop.org/wiki/Software/systemd/inhibit */
+static void virNetServerCallInhibit(virNetServerPtr srv,
+                                    const char *what,
+                                    const char *who,
+                                    const char *why,
+                                    const char *mode)
+{
+    DBusMessage *message;
+    DBusPendingCall *pendingReply;
+    DBusConnection *systemBus;
+
+    VIR_DEBUG("srv=%p what=%s who=%s why=%s mode=%s",
+              srv, NULLSTR(what), NULLSTR(who), NULLSTR(why), NULLSTR(mode));
+
+    if (!(systemBus = virDBusGetSystemBus()))
+        return;
+
+    /* Only one outstanding call at a time */
+    if (srv->autoShutdownCallingInhibit)
+        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,
+                                     virNetServerGotInhibitReply,
+                                     srv, NULL);
+        srv->autoShutdownCallingInhibit = true;
+    }
+    dbus_message_unref(message);
+}
+#endif
+
 void virNetServerAddShutdownInhibition(virNetServerPtr srv)
 {
     virNetServerLock(srv);
     srv->autoShutdownInhibitions++;
+
+    VIR_DEBUG("srv=%p inhibitions=%zu", srv, srv->autoShutdownInhibitions);
+
+#ifdef HAVE_DBUS
+    if (srv->autoShutdownInhibitions == 1)
+        virNetServerCallInhibit(srv,
+                                "shutdown",
+                                _("Libvirt"),
+                                _("Virtual machines need to be saved"),
+                                "delay");
+#endif
+
     virNetServerUnlock(srv);
 }
 
@@ -728,6 +830,12 @@ void virNetServerRemoveShutdownInhibition(virNetServerPtr srv)
 {
     virNetServerLock(srv);
     srv->autoShutdownInhibitions--;
+
+    VIR_DEBUG("srv=%p inhibitions=%zu", srv, srv->autoShutdownInhibitions);
+
+    if (srv->autoShutdownInhibitions == 0)
+        VIR_FORCE_CLOSE(srv->autoShutdownInhibitFd);
+
     virNetServerUnlock(srv);
 }
 
@@ -1065,6 +1173,8 @@ void virNetServerDispose(void *obj)
     virNetServerPtr srv = obj;
     int i;
 
+    VIR_FORCE_CLOSE(srv->autoShutdownInhibitFd);
+
     for (i = 0 ; i < srv->nservices ; i++)
         virNetServerServiceToggle(srv->services[i], false);
 
-- 
1.7.12.1




More information about the libvir-list mailing list