Blob Blame History Raw
From 1dcab6586cd1922376031243b354a6a86b7013a5 Mon Sep 17 00:00:00 2001
From: Natalie Clarius <natalie_clarius@yahoo.de>
Date: Thu, 31 Aug 2023 17:04:52 +0200
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.

BUG: 473835
FIXED-IN: 6.0
---
 .../profilegenerator/generate_profiles.cpp      |  4 +++-
 daemon/powerdevilcore.cpp                       |  4 +++-
 daemon/powerdevilpowermanagement.cpp            | 17 +++++++++++++++++
 daemon/powerdevilpowermanagement.h              |  1 +
 daemon/powerdevilprofiledefaults.cpp            | 14 ++++++++++++--
 daemon/powerdevilprofiledefaults.h              |  4 ++--
 daemon/powerdevilprofilegenerator.cpp           |  8 ++++----
 daemon/powerdevilprofilegenerator.h             |  3 +--
 kcmodule/profiles/EditPage.cpp                  |  2 ++
 9 files changed, 45 insertions(+), 12 deletions(-)

diff --git a/autotests/profilegenerator/generate_profiles.cpp b/autotests/profilegenerator/generate_profiles.cpp
index ef6a6c309..f06309502 100644
--- a/autotests/profilegenerator/generate_profiles.cpp
+++ b/autotests/profilegenerator/generate_profiles.cpp
@@ -31,6 +31,7 @@ int main(int argc, char **argv)
                        "powermanagementprofilesrc in your XDG_CONFIG_HOME, but this tool can produce a brand-new one under any name."));
     QCommandLineOption optionMobile("mobile",
                                     "Generate profiles for a mobile device (i.e. phones, tablets running Plasma Mobile) instead of regular desktop/laptop.");
+    QCommandLineOption optionVM("vm", "Generate profiles for a virtual machine environment instead of bare metal.");
     QCommandLineOption optionCannotSuspendToRam("cannot-suspend-to-ram", "Assume that the device does not support suspending to RAM a.k.a. Sleep.");
     QCommandLineOption optionCannotSuspendToDisk("cannot-suspend-to-disk", "Assume that the device does not support suspending to disk a.k.a. Hibernate.");
     parser.addOptions({optionMobile, optionCannotSuspendToRam, optionCannotSuspendToDisk});
@@ -42,6 +43,7 @@ int main(int argc, char **argv)
     }
 
     bool isMobile = parser.isSet(optionMobile);
+    bool isVM = parser.isSet(optionVM);
     bool canSuspendToRam = !parser.isSet(optionCannotSuspendToRam);
     bool canSuspendToDisk = !parser.isSet(optionCannotSuspendToDisk);
 
@@ -55,7 +57,7 @@ int main(int argc, char **argv)
         QFile::remove(temp_globalrc_path);
         QFile::remove(parser.positionalArguments()[0]);
 
-        PowerDevil::ProfileGenerator::generateProfiles(isMobile, canSuspendToRam, canSuspendToDisk);
+        PowerDevil::ProfileGenerator::generateProfiles(isMobile, isVM, canSuspendToRam, canSuspendToDisk);
 
         if (!QFile::rename(temp_profilesrc_path, parser.positionalArguments()[0])) {
             qDebug() << "Unable to move config file to destination.";
diff --git a/daemon/powerdevilcore.cpp b/daemon/powerdevilcore.cpp
index fc0087188..1392bb7ea 100644
--- a/daemon/powerdevilcore.cpp
+++ b/daemon/powerdevilcore.cpp
@@ -13,6 +13,7 @@
 #include "powerdevilactionpool.h"
 #include "powerdevilenums.h"
 #include "powerdevilpolicyagent.h"
+#include "powerdevilpowermanagement.h"
 #include "powerdevilprofilegenerator.h"
 
 #include <Solid/Battery>
@@ -108,8 +109,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 2e3d85ce9..1f42c82ab 100644
--- a/daemon/powerdevilpowermanagement.cpp
+++ b/daemon/powerdevilpowermanagement.cpp
@@ -5,6 +5,7 @@
 */
 
 #include "powerdevilpowermanagement.h"
+#include "powerdevil_debug.h"
 
 #include <QDBusConnection>
 #include <QDBusConnectionInterface>
@@ -196,6 +197,22 @@ void PowerManagement::suspendThenHibernate()
     QDBusConnection::sessionBus().asyncCall(message);
 }
 
+bool PowerManagement::isVirtualMachine()
+{
+    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 6f4abce65..1e2a4dc8d 100644
--- a/daemon/powerdevilpowermanagement.h
+++ b/daemon/powerdevilpowermanagement.h
@@ -22,6 +22,7 @@ class POWERDEVILCORE_EXPORT PowerManagement : public QObject
 public:
     ~PowerManagement() override;
 
+    bool isVirtualMachine();
     bool canSuspend() const;
     bool canHibernate() const;
     bool canHybridSuspend() const;
diff --git a/daemon/powerdevilprofiledefaults.cpp b/daemon/powerdevilprofiledefaults.cpp
index eb4f79045..e3b5bc8c8 100644
--- a/daemon/powerdevilprofiledefaults.cpp
+++ b/daemon/powerdevilprofiledefaults.cpp
@@ -75,8 +75,13 @@ bool ProfileDefaults::defaultLockBeforeTurnOffDisplay(bool isMobile)
     return isMobile;
 }
 
-bool ProfileDefaults::defaultAutoSuspendWhenIdle(bool canSuspendToRam)
+bool ProfileDefaults::defaultAutoSuspendWhenIdle(bool isVM, bool canSuspendToRam)
 {
+    // Don't auto suspend by default when running in a virtual machine as it can cause hangs,
+    // see bug 473835
+    if (isVM) {
+        return false;
+    }
     // Even on AC power, suspend after a rather long period of inactivity. Energy is precious!
     return canSuspendToRam;
 }
@@ -111,8 +116,13 @@ unsigned int ProfileDefaults::defaultPowerDownAction()
     return qToUnderlying(PowerButtonAction::PromptLogoutDialog);
 }
 
-unsigned int ProfileDefaults::defaultLidAction(bool canSuspendToRam)
+unsigned int ProfileDefaults::defaultLidAction(bool isVM, bool canSuspendToRam)
 {
+    // Don't auto suspend by default when running in a virtual machine as it can cause hangs,
+    // see bug 473835
+    if (isVM) {
+        return qToUnderlying(PowerButtonAction::NoAction);
+    }
     return qToUnderlying(canSuspendToRam ? PowerButtonAction::SuspendToRam : PowerButtonAction::TurnOffScreen);
 }
 
diff --git a/daemon/powerdevilprofiledefaults.h b/daemon/powerdevilprofiledefaults.h
index b874ed3b3..e9c8f570a 100644
--- a/daemon/powerdevilprofiledefaults.h
+++ b/daemon/powerdevilprofiledefaults.h
@@ -27,13 +27,13 @@ public:
     static int defaultTurnOffDisplayIdleTimeoutSec(const QString &profileGroup, bool isMobile);
     static bool defaultLockBeforeTurnOffDisplay(bool isMobile);
 
-    static bool defaultAutoSuspendWhenIdle(bool canSuspendToRam);
+    static bool defaultAutoSuspendWhenIdle(bool isVM, bool canSuspendToRam);
     static int defaultAutoSuspendIdleTimeoutSec(const QString &profileGroup, bool isMobile);
     static unsigned int defaultAutoSuspendType();
 
     static unsigned int defaultPowerButtonAction(bool isMobile);
     static unsigned int defaultPowerDownAction();
-    static unsigned int defaultLidAction(bool canSuspendToRam);
+    static unsigned int defaultLidAction(bool isVM, bool canSuspendToRam);
 };
 
 }
diff --git a/daemon/powerdevilprofilegenerator.cpp b/daemon/powerdevilprofilegenerator.cpp
index 1c0202c2c..30b0c2185 100644
--- a/daemon/powerdevilprofilegenerator.cpp
+++ b/daemon/powerdevilprofilegenerator.cpp
@@ -19,7 +19,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) {
@@ -44,7 +44,7 @@ void ProfileGenerator::generateProfiles(bool mobile, bool toRam, bool toDisk)
         }
     }
 
-    auto initProfile = [toRam, mobile](KConfigGroup &profile) {
+    auto initProfile = [toRam, mobile, vm](KConfigGroup &profile) {
         if (ProfileDefaults::defaultUseProfileSpecificDisplayBrightness(profile.name())) {
             KConfigGroup brightnessControl(&profile, "BrightnessControl");
             brightnessControl.writeEntry("value", ProfileDefaults::defaultDisplayBrightness(profile.name()));
@@ -53,7 +53,7 @@ void ProfileGenerator::generateProfiles(bool mobile, bool toRam, bool toDisk)
         KConfigGroup handleButtonEvents(&profile, "HandleButtonEvents");
         handleButtonEvents.writeEntry("powerButtonAction", ProfileDefaults::defaultPowerButtonAction(mobile));
         handleButtonEvents.writeEntry("powerDownAction", ProfileDefaults::defaultPowerDownAction());
-        handleButtonEvents.writeEntry("lidAction", ProfileDefaults::defaultLidAction(toRam));
+        handleButtonEvents.writeEntry("lidAction", ProfileDefaults::defaultLidAction(vm, toRam));
 
         if (ProfileDefaults::defaultDimDisplayWhenIdle()) {
             KConfigGroup dimDisplay(&profile, "DimDisplay");
@@ -66,7 +66,7 @@ void ProfileGenerator::generateProfiles(bool mobile, bool toRam, bool toDisk)
             dpmsControl.writeEntry<int>("lockBeforeTurnOff", ProfileDefaults::defaultLockBeforeTurnOffDisplay(mobile));
         }
 
-        if (ProfileDefaults::defaultAutoSuspendWhenIdle(toRam)) {
+        if (ProfileDefaults::defaultAutoSuspendWhenIdle(vm, toRam)) {
             KConfigGroup suspendSession(&profile, "SuspendSession");
             suspendSession.writeEntry("idleTime", ProfileDefaults::defaultAutoSuspendIdleTimeoutSec(profile.name(), mobile) * 1000); // milliseconds
             suspendSession.writeEntry("suspendType", ProfileDefaults::defaultAutoSuspendType());
diff --git a/daemon/powerdevilprofilegenerator.h b/daemon/powerdevilprofilegenerator.h
index bd92f3dc3..b29f5d06d 100644
--- a/daemon/powerdevilprofilegenerator.h
+++ b/daemon/powerdevilprofilegenerator.h
@@ -13,7 +13,6 @@ namespace PowerDevil
 namespace ProfileGenerator
 {
 
-void POWERDEVILCORE_EXPORT generateProfiles(bool isMobile, bool toRam, bool toDisk);
-
+void POWERDEVILCORE_EXPORT generateProfiles(bool isMobile, bool isVM, bool toRam, bool toDisk);
 }
 }
diff --git a/kcmodule/profiles/EditPage.cpp b/kcmodule/profiles/EditPage.cpp
index 15210f342..0aa3d65ba 100644
--- a/kcmodule/profiles/EditPage.cpp
+++ b/kcmodule/profiles/EditPage.cpp
@@ -53,6 +53,7 @@ EditPage::EditPage(QObject *parent, const KPluginMetaData &data)
         auto interface = Kirigami::TabletModeWatcher::self();
 
         PowerDevil::ProfileGenerator::generateProfiles(interface->isTabletMode(),
+                                                       PowerDevil::PowerManagement::instance()->isVirtualMachine(),
                                                        PowerDevil::PowerManagement::instance()->canSuspend(),
                                                        PowerDevil::PowerManagement::instance()->canHibernate());
         m_profilesConfig->reparseConfiguration();
@@ -166,6 +167,7 @@ void EditPage::restoreDefaultProfiles()
         auto interface = Kirigami::TabletModeWatcher::self();
 
         PowerDevil::ProfileGenerator::generateProfiles(interface->isTabletMode(),
+                                                       PowerDevil::PowerManagement::instance()->isVirtualMachine(),
                                                        PowerDevil::PowerManagement::instance()->canSuspend(),
                                                        PowerDevil::PowerManagement::instance()->canHibernate());
 
-- 
GitLab