]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
Inhibit desktop shutdown while any virtual machines are running
authorDaniel P. Berrange <berrange@redhat.com>
Wed, 31 Oct 2012 19:03:56 +0000 (19:03 +0000)
committerDaniel P. Berrange <berrange@redhat.com>
Tue, 4 Dec 2012 12:14:04 +0000 (12:14 +0000)
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@redhat.com>
src/rpc/virnetserver.c

index b10839933bc3ab8b2fd45ef7b72d1beadf8a6c7e..ae1dfa20275304fc6ee22418b414950ab3e6282f 100644 (file)
@@ -37,6 +37,7 @@
 #include "virfile.h"
 #include "event.h"
 #include "virnetservermdns.h"
+#include "virdbus.h"
 
 #ifndef SA_SIGINFO
 # define SA_SIGINFO 0
@@ -102,6 +103,8 @@ struct _virNetServer {
 
     unsigned int autoShutdownTimeout;
     size_t autoShutdownInhibitions;
+    bool autoShutdownCallingInhibit;
+    int autoShutdownInhibitFd;
 
     virNetServerClientPrivNew clientPrivNew;
     virNetServerClientPrivPreExecRestart clientPrivPreExecRestart;
@@ -390,7 +393,8 @@ virNetServerPtr virNetServerNew(size_t min_workers,
     srv->clientPrivPreExecRestart = clientPrivPreExecRestart;
     srv->clientPrivFree = clientPrivFree;
     srv->clientPrivOpaque = clientPrivOpaque;
-    srv->privileged = geteuid() == 0 ? true : false;
+    srv->privileged = geteuid() == 0;
+    srv->autoShutdownInhibitFd = -1;
 
     if (mdnsGroupName &&
         !(srv->mdnsGroupName = strdup(mdnsGroupName))) {
@@ -716,10 +720,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 +826,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 +1169,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);