]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
util: introduce object for holding a system inhibitor lock
authorDaniel P. Berrangé <berrange@redhat.com>
Mon, 16 Dec 2024 15:19:34 +0000 (15:19 +0000)
committerDaniel P. Berrangé <berrange@redhat.com>
Thu, 19 Dec 2024 18:03:04 +0000 (18:03 +0000)
The system inhibitor locks are currently handled by code in the
virNetDaemon class. The driver code invokes a callback provided
by the daemon when it wants to start or end inhibition.

When the first inhibition is started, the daemon will call out
to logind to apply it system wide.

This has many flaws

 * A single message is registered with logind regardless of
   what driver holds the inhibition
 * An inhibition of daemon shutdown can't be acquired
   without also inhibiting system shutdown
 * Config of the inhibitions cannot be tailored by the
   driver

The new virInhibitor object addresses these:

 * The object directly manages an inhibition with logind
   privately to the driver, enabling custom messages to
   be set.
 * It is possible to acquire an inhibition locally to the
   daemon without forwarding it to logind.

Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
po/POTFILES
src/libvirt_private.syms
src/util/meson.build
src/util/virinhibitor.c [new file with mode: 0644]
src/util/virinhibitor.h [new file with mode: 0644]

index 3514aa3dcad741e398aa0f91c0c9ff9634ebfed4..c71e439fe362361744fe98f180558435be697a88 100644 (file)
@@ -293,6 +293,7 @@ src/util/virhostcpu.c
 src/util/virhostmem.c
 src/util/virhostuptime.c
 src/util/viridentity.c
+src/util/virinhibitor.c
 src/util/virinitctl.c
 src/util/viriscsi.c
 src/util/virjson.c
index c931003fadd2763b02e058ae13e767e36994f79b..adc3e3064f91ab5fe48a13497b7d1f8c47f20055 100644 (file)
@@ -2608,6 +2608,13 @@ virIdentitySetUserName;
 virIdentitySetX509DName;
 
 
+# util/virinhibitor.h
+virInhibitorFree;
+virInhibitorHold;
+virInhibitorNew;
+virInhibitorRelease;
+
+
 # util/virinitctl.h
 virInitctlFifos;
 virInitctlSetRunLevel;
index 30f71b022779f83bc9fd234451a39092e8c01da0..69ef49139a4d69f8b886f5b8bed3d6a5337997aa 100644 (file)
@@ -45,6 +45,7 @@ util_sources = [
   'virhostmem.c',
   'virhostuptime.c',
   'viridentity.c',
+  'virinhibitor.c',
   'virinitctl.c',
   'viriscsi.c',
   'virjson.c',
diff --git a/src/util/virinhibitor.c b/src/util/virinhibitor.c
new file mode 100644 (file)
index 0000000..647bdc9
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * virinhibitor.c: helper APIs for inhibiting host actions
+ *
+ * Copyright (C) 2024 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "virinhibitor.h"
+#include "virgdbus.h"
+#include "virsystemd.h"
+#include "virfile.h"
+#include "virlog.h"
+#include "virenum.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+VIR_LOG_INIT("util.inhibitor");
+
+struct _virInhibitor {
+    GMutex lock;
+    size_t count;
+    int fd;
+
+    char *what;
+    char *who;
+    char *why;
+    const char *mode;
+
+    virInhibitorAction action;
+    void *actionData;
+};
+
+VIR_ENUM_DECL(virInhibitorMode);
+
+VIR_ENUM_IMPL(virInhibitorMode,
+              VIR_INHIBITOR_MODE_LAST,
+              "block", "delay");
+
+#ifdef G_OS_UNIX
+/* As per: https://www.freedesktop.org/wiki/Software/systemd/inhibit */
+static int
+virInhibitorAcquire(const char *what,
+                    const char *who,
+                    const char *why,
+                    const char *mode,
+                    int *inhibitorFD)
+{
+    g_autoptr(GVariant) reply = NULL;
+    g_autoptr(GUnixFDList) replyFD = NULL;
+    g_autoptr(GVariant) message = NULL;
+    GDBusConnection *systemBus;
+    int fd;
+    int rc;
+
+    VIR_DEBUG("what=%s who=%s why=%s mode=%s",
+              NULLSTR(what), NULLSTR(who), NULLSTR(why), NULLSTR(mode));
+
+    if (!(systemBus = virGDBusGetSystemBus())) {
+        VIR_DEBUG("system dbus not available, skipping system inhibitor");
+        return 0;
+    }
+
+    if (virSystemdHasLogind() < 0) {
+        VIR_DEBUG("logind not available, skipping system inhibitor");
+        return 0;
+    }
+
+    message = g_variant_new("(ssss)", what, who, why, mode);
+
+    rc = virGDBusCallMethodWithFD(systemBus,
+                                  &reply,
+                                  G_VARIANT_TYPE("(h)"),
+                                  &replyFD,
+                                  NULL,
+                                  "org.freedesktop.login1",
+                                  "/org/freedesktop/login1",
+                                  "org.freedesktop.login1.Manager",
+                                  "Inhibit",
+                                  message,
+                                  NULL);
+
+    if (rc < 0)
+        return -1;
+
+    if (g_unix_fd_list_get_length(replyFD) <= 0) {
+        VIR_DEBUG("Missing inhibitor FD in logind reply");
+        return -1;
+    }
+
+    fd = g_unix_fd_list_get(replyFD, 0, NULL);
+    if (fd < 0) {
+        VIR_DEBUG("Unable to get inhibitor FD from logind reply");
+        return -1;
+    }
+
+    *inhibitorFD = fd;
+    VIR_DEBUG("Got inhibitor FD %d", fd);
+    return 0;
+}
+#endif
+
+
+static char *
+virInhibitorWhatFormat(virInhibitorWhat what)
+{
+    const char *whatstr[] = {
+        "sleep",
+        "shutdown",
+        "idle",
+        "handle-power-key",
+        "handle-suspend-key",
+        "handle-hibernate-key",
+        "handle-lid-switch",
+    };
+    GString *str = g_string_new("");
+    size_t i;
+
+    for (i = 0; i < G_N_ELEMENTS(whatstr); i++) {
+        if (what & (1 << i)) {
+            if (str->len)
+                g_string_append(str, ":");
+            g_string_append(str, whatstr[i]);
+        }
+    }
+
+    return g_string_free(str, FALSE);
+}
+
+
+virInhibitor *virInhibitorNew(virInhibitorWhat what,
+                              const char *who,
+                              const char *why,
+                              virInhibitorMode mode,
+                              virInhibitorAction action,
+                              void *actionData)
+{
+    virInhibitor *inhibitor = g_new0(virInhibitor, 1);
+
+    inhibitor->fd = -1;
+    inhibitor->what = virInhibitorWhatFormat(what);
+    inhibitor->who = g_strdup(who);
+    inhibitor->why = g_strdup(why);
+    inhibitor->mode = virInhibitorModeTypeToString(mode);
+    inhibitor->action = action;
+    inhibitor->actionData = actionData;
+
+    return inhibitor;
+}
+
+void virInhibitorHold(virInhibitor *inhibitor)
+{
+    g_mutex_lock(&inhibitor->lock);
+
+    if (inhibitor->count == 0) {
+        if (inhibitor->action) {
+            inhibitor->action(true, inhibitor->actionData);
+        }
+#ifdef G_OS_UNIX
+        if (virInhibitorAcquire(
+                inhibitor->what, inhibitor->who, inhibitor->why,
+                inhibitor->mode, &inhibitor->fd) < 0) {
+            VIR_ERROR(_("Failed to acquire inhibitor: %1$s"),
+                      virGetLastErrorMessage());
+            virResetLastError();
+        }
+#else
+        VIR_DEBUG("No inhibitor implementation on non-UNIX platforms");
+#endif
+    }
+    inhibitor->count++;
+    g_mutex_unlock(&inhibitor->lock);
+}
+
+
+void virInhibitorRelease(virInhibitor *inhibitor)
+{
+    g_mutex_lock(&inhibitor->lock);
+    inhibitor->count--;
+    if (inhibitor->count == 0) {
+        VIR_FORCE_CLOSE(inhibitor->fd);
+        if (inhibitor->action) {
+            inhibitor->action(false, inhibitor->actionData);
+        }
+    }
+    g_mutex_unlock(&inhibitor->lock);
+}
+
+
+void virInhibitorFree(virInhibitor *inhibitor)
+{
+    if (!inhibitor)
+        return;
+
+    g_free(inhibitor->what);
+    g_free(inhibitor->who);
+    g_free(inhibitor->why);
+    VIR_FORCE_CLOSE(inhibitor->fd);
+    g_free(inhibitor);
+}
diff --git a/src/util/virinhibitor.h b/src/util/virinhibitor.h
new file mode 100644 (file)
index 0000000..0a1c445
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * virinhibitor.h: helper APIs for inhibiting host actions
+ *
+ * Copyright (C) 2024 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "internal.h"
+
+typedef struct _virInhibitor virInhibitor;
+
+typedef enum {
+    VIR_INHIBITOR_WHAT_NONE          = 0,
+    VIR_INHIBITOR_WHAT_SLEEP         = (1 << 1),
+    VIR_INHIBITOR_WHAT_SHUTDOWN      = (1 << 2),
+    VIR_INHIBITOR_WHAT_IDLE          = (1 << 3),
+    VIR_INHIBITOR_WHAT_POWER_KEY     = (1 << 4),
+    VIR_INHIBITOR_WHAT_SUSPEND_KEY   = (1 << 5),
+    VIR_INHIBITOR_WHAT_HIBERNATE_KEY = (1 << 6),
+    VIR_INHIBITOR_WHAT_LID_SWITCH    = (1 << 7),
+} virInhibitorWhat;
+
+typedef enum {
+    VIR_INHIBITOR_MODE_BLOCK,
+    VIR_INHIBITOR_MODE_DELAY,
+
+    VIR_INHIBITOR_MODE_LAST
+} virInhibitorMode;
+
+typedef void (*virInhibitorAction)(bool inhibited,
+                                   void *opaque);
+
+virInhibitor *virInhibitorNew(virInhibitorWhat what,
+                              const char *who,
+                              const char *why,
+                              virInhibitorMode mode,
+                              virInhibitorAction action,
+                              void *actionData);
+
+void virInhibitorHold(virInhibitor *inhibitor);
+void virInhibitorRelease(virInhibitor *inhibitor);
+
+void virInhibitorFree(virInhibitor *inhibitor);