Blob Blame History Raw
From 5a523543de6bffc0f6d78eab1a6bf27c1454d752 Mon Sep 17 00:00:00 2001
From: Adam Williamson <awilliam@redhat.com>
Date: Fri, 1 Sep 2023 17:09:16 -0700
Subject: [PATCH] profiledefaults: don't automatically suspend by default if
 running in a virtual machine

To avoid hangs in virtual environments which don't suppport suspension.
https://invent.kde.org/plasma/powerdevil/-/merge_requests/230
from Natalie Clarius, rebased on 5.27.7.

Signed-off-by: Adam Williamson <awilliam@redhat.com>
---
 daemon/powerdevilcore.cpp             |  4 +++-
 daemon/powerdevilpowermanagement.cpp  | 21 +++++++++++++++++++++
 daemon/powerdevilpowermanagement.h    |  1 +
 daemon/powerdevilprofilegenerator.cpp | 17 ++++++++++-------
 daemon/powerdevilprofilegenerator.h   |  2 +-
 kcmodule/profiles/EditPage.cpp        |  2 ++
 6 files changed, 38 insertions(+), 9 deletions(-)

diff --git a/daemon/powerdevilcore.cpp b/daemon/powerdevilcore.cpp
index 0791552c..5c4f8a7d 100644
--- a/daemon/powerdevilcore.cpp
+++ b/daemon/powerdevilcore.cpp
@@ -24,6 +24,7 @@
 #include "powerdevilaction.h"
 #include "powerdevilactionpool.h"
 #include "powerdevilpolicyagent.h"
+#include "powerdevilpowermanagement.h"
 #include "powerdevilprofilegenerator.h"
 #include "powerdevil_debug.h"
 
@@ -126,8 +127,9 @@ void Core::onBackendReady()
 
         // These are generated profiles,
         const bool mobile = Kirigami::TabletModeWatcher::self()->isTabletMode();
+        const bool vm = PowerDevil::PowerManagement::instance()->isVirtualMachine();
 
-        ProfileGenerator::generateProfiles(mobile, toRam, toDisk);
+        ProfileGenerator::generateProfiles(mobile, vm, toRam, toDisk);
         m_profilesConfig->reparseConfiguration();
     }
 
diff --git a/daemon/powerdevilpowermanagement.cpp b/daemon/powerdevilpowermanagement.cpp
index 7b8e8d5d..573a6a7d 100644
--- a/daemon/powerdevilpowermanagement.cpp
+++ b/daemon/powerdevilpowermanagement.cpp
@@ -19,6 +19,7 @@ License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 *********************************************************************/
 
 #include "powerdevilpowermanagement.h"
+#include "powerdevil_debug.h"
 
 #include <QDBusConnection>
 #include <QDBusConnectionInterface>
@@ -217,6 +218,26 @@ void PowerManagement::suspendThenHibernate()
     QDBusConnection::sessionBus().asyncCall(message);
 }
 
+bool PowerManagement::isVirtualMachine()
+{
+    if (!QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.systemd1"))) {
+        // can't check, fall back to assuming false
+        return false;
+    }
+    QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"),
+                                                          QStringLiteral("/org/freedesktop/systemd1"),
+                                                          QStringLiteral("org.freedesktop.DBus.Properties"),
+                                                          QStringLiteral("Get"));
+    message.setArguments({QStringLiteral("org.freedesktop.systemd1.Manager"), QStringLiteral("Virtualization")});
+    QDBusReply<QDBusVariant> reply = QDBusConnection::systemBus().call(message);
+    if (!reply.isValid() || reply.value().variant().isNull() || reply.value().variant().toString().isNull()) {
+        qCWarning(POWERDEVIL) << "Failed to get property Virtualization from systemd1 DBus service:" << reply.error().message();
+        return false;
+    }
+    /* on bare-metal hardware this is the empty string, otherwise an identifier such as "kvm", "vmware", etc. */
+    return !reply.value().variant().toString().isEmpty();
+}
+
 bool PowerManagement::canSuspend() const
 {
     return d->canSuspend;
diff --git a/daemon/powerdevilpowermanagement.h b/daemon/powerdevilpowermanagement.h
index 84c99f19..14069708 100644
--- a/daemon/powerdevilpowermanagement.h
+++ b/daemon/powerdevilpowermanagement.h
@@ -35,6 +35,7 @@ class Q_DECL_EXPORT PowerManagement : public QObject
 public:
     ~PowerManagement() override;
 
+    bool isVirtualMachine();
     bool canSuspend() const;
     bool canHibernate() const;
     bool canHybridSuspend() const;
diff --git a/daemon/powerdevilprofilegenerator.cpp b/daemon/powerdevilprofilegenerator.cpp
index b3029e02..475b9ed6 100644
--- a/daemon/powerdevilprofilegenerator.cpp
+++ b/daemon/powerdevilprofilegenerator.cpp
@@ -29,7 +29,7 @@
 
 namespace PowerDevil {
 
-void ProfileGenerator::generateProfiles(bool mobile, bool toRam, bool toDisk)
+void ProfileGenerator::generateProfiles(bool mobile, bool vm, bool toRam, bool toDisk)
 {
     // Change critical action if default (hibernate) is unavailable
     if (!toDisk) {
@@ -64,14 +64,17 @@ void ProfileGenerator::generateProfiles(bool mobile, bool toRam, bool toDisk)
         dimDisplay.writeEntry< int >("idleTime", 300000);
     }
 
-    auto initLid = [toRam, mobile](KConfigGroup &profile)
+    auto initLid = [vm, toRam, mobile](KConfigGroup &profile)
     {
         const Modes defaultPowerButtonAction = mobile ? ToggleScreenOnOffMode : LogoutDialogMode;
 
         KConfigGroup handleButtonEvents(&profile, "HandleButtonEvents");
         handleButtonEvents.writeEntry< uint >("powerButtonAction", defaultPowerButtonAction);
         handleButtonEvents.writeEntry< uint >("powerDownAction", LogoutDialogMode);
-        if (toRam) {
+        if (vm) {
+            handleButtonEvents.writeEntry< uint >("lidAction", NoneMode);
+        }
+        else if (toRam) {
             handleButtonEvents.writeEntry< uint >("lidAction", ToRamMode);
         } else {
             handleButtonEvents.writeEntry< uint >("lidAction", TurnOffScreenMode);
@@ -91,8 +94,8 @@ void ProfileGenerator::generateProfiles(bool mobile, bool toRam, bool toDisk)
     }
 
     // Even on AC power, suspend after a rather long period of inactivity. Energy
-    // is precious!
-    if (toRam) {
+    // is precious! But not on VMs.
+    if (toRam && !vm) {
         // on mobile, 7 minutes, on laptop 15 minutes
         auto timeout = mobile ? 420000 : 900000;
         KConfigGroup suspendSession(&acProfile, "SuspendSession");
@@ -124,7 +127,7 @@ void ProfileGenerator::generateProfiles(bool mobile, bool toRam, bool toDisk)
     }
 
     // Last but not least, we want to suspend after some inactivity
-    if (toRam) {
+    if (toRam && !vm) {
         // on mobile, 5 minute, on laptop 10 minutes
         auto timeout = mobile ? 300000 : 600000;
         KConfigGroup suspendSession(&batteryProfile, "SuspendSession");
@@ -164,7 +167,7 @@ void ProfileGenerator::generateProfiles(bool mobile, bool toRam, bool toDisk)
     // Last but not least, we want to suspend after a rather long period of inactivity
     // on mobile by default never suspend, if device wants to suspend, it will enable
     // using configuration overlay
-    if (toRam) {
+    if (toRam && !vm) {
         // config is in the miliseconds
         KConfigGroup suspendSession(&lowBatteryProfile, "SuspendSession");
         suspendSession.writeEntry< uint >("idleTime", 300000);
diff --git a/daemon/powerdevilprofilegenerator.h b/daemon/powerdevilprofilegenerator.h
index 481caa73..6f5ffb48 100644
--- a/daemon/powerdevilprofilegenerator.h
+++ b/daemon/powerdevilprofilegenerator.h
@@ -36,7 +36,7 @@ namespace ProfileGenerator
         ToggleScreenOnOffMode = 128
     };
 
-    void generateProfiles(bool isMobile, bool toRam, bool toDisk);
+    void generateProfiles(bool isMobile, bool isVM, bool toRam, bool toDisk);
 }
 
 }
diff --git a/kcmodule/profiles/EditPage.cpp b/kcmodule/profiles/EditPage.cpp
index 22ed5d0e..119d818e 100644
--- a/kcmodule/profiles/EditPage.cpp
+++ b/kcmodule/profiles/EditPage.cpp
@@ -79,6 +79,7 @@ EditPage::EditPage(QWidget *parent, const QVariantList &args)
 
         PowerDevil::ProfileGenerator::generateProfiles(
             interface->isTabletMode(),
+            PowerDevil::PowerManagement::instance()->isVirtualMachine(),
             PowerDevil::PowerManagement::instance()->canSuspend(),
             PowerDevil::PowerManagement::instance()->canHibernate()
         );
@@ -198,6 +199,7 @@ void EditPage::restoreDefaultProfiles()
 
         PowerDevil::ProfileGenerator::generateProfiles(
             interface->isTabletMode(),
+            PowerDevil::PowerManagement::instance()->isVirtualMachine(),
             PowerDevil::PowerManagement::instance()->canSuspend(),
             PowerDevil::PowerManagement::instance()->canHibernate()
         );
-- 
2.41.0