Merge pull request #2404 from jpetso/work/jpetso/fix-2384

[libcalamares] Block sleep with systemd/CK login manager if present
This commit is contained in:
Adriaan de Groot 2024-12-07 22:27:31 +01:00 committed by GitHub
commit 714a085556
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -17,6 +17,7 @@
#include "utils/Logger.h" #include "utils/Logger.h"
#include <QDBusConnection> #include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusMessage> #include <QDBusMessage>
#include <QDBusPendingCall> #include <QDBusPendingCall>
#include <QDBusPendingReply> #include <QDBusPendingReply>
@ -24,6 +25,8 @@
#include <QThread> #include <QThread>
#include <memory> #include <memory>
#include <optional>
#include <unistd.h> // for close()
namespace namespace
{ {
@ -36,7 +39,7 @@ namespace
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
/** @brief Class to manage sleep / suspend on inactivity /** @brief Class to manage sleep / suspend on inactivity (via fd.o Inhibit service on the user bus)
* *
* Create an object of this class on the heap. Call inhibitSleep() * Create an object of this class on the heap. Call inhibitSleep()
* to (try to) stop system sleep / suspend. Call uninhibitSleep() * to (try to) stop system sleep / suspend. Call uninhibitSleep()
@ -166,20 +169,166 @@ PowerManagementInterface::uninhibitSleep()
replyWatcher, &QDBusPendingCallWatcher::finished, this, &PowerManagementInterface::uninhibitDBusCallFinished ); replyWatcher, &QDBusPendingCallWatcher::finished, this, &PowerManagementInterface::uninhibitDBusCallFinished );
} }
/** @brief Class to manage sleep / suspend on inactivity (via logind/CK2 service on the system bus)
*
* Create an object of this class on the heap. Call inhibitSleep()
* to (try to) stop system sleep / suspend. Call uninhibitSleep()
* when the object is no longer needed. The object self-deletes
* after uninhibitSleep() completes.
*/
class LoginManagerInterface : public QObject
{
Q_OBJECT
public:
static LoginManagerInterface* makeForRegisteredService( QObject* parent = nullptr );
~LoginManagerInterface() override;
public Q_SLOTS:
void inhibitSleep();
void uninhibitSleep();
private Q_SLOTS:
void inhibitDBusCallFinished( QDBusPendingCallWatcher* aWatcher );
private:
enum class Service
{
Logind,
ConsoleKit,
};
LoginManagerInterface( Service service, QObject* parent = nullptr );
int m_inhibitFd = -1;
Service m_service;
};
LoginManagerInterface*
LoginManagerInterface::makeForRegisteredService( QObject* parent )
{
if ( QDBusConnection::systemBus().interface()->isServiceRegistered( QStringLiteral( "org.freedesktop.login1" ) ) )
{
return new LoginManagerInterface( Service::Logind, parent );
}
else if ( QDBusConnection::systemBus().interface()->isServiceRegistered(
QStringLiteral( "org.freedesktop.ConsoleKit" ) ) )
{
return new LoginManagerInterface( Service::ConsoleKit, parent );
}
else
{
return nullptr;
}
}
LoginManagerInterface::LoginManagerInterface( Service service, QObject* parent )
: QObject( parent )
, m_service( service )
{
}
LoginManagerInterface::~LoginManagerInterface() = default;
void
LoginManagerInterface::inhibitDBusCallFinished( QDBusPendingCallWatcher* aWatcher )
{
QDBusPendingReply< uint > reply = *aWatcher;
if ( reply.isError() )
{
cError() << "Could not inhibit sleep:" << reply.error();
// m_inhibitFd = -1; // unchanged
}
else
{
m_inhibitFd = reply.argumentAt< 0 >();
cDebug() << "Sleep inhibited, file descriptor" << m_inhibitFd;
}
aWatcher->deleteLater();
}
void
LoginManagerInterface::inhibitSleep()
{
if ( m_inhibitFd == -1 )
{
cDebug() << "Sleep is already inhibited.";
return;
}
auto systemBus = QDBusConnection::systemBus();
QDBusMessage inhibitCall;
if ( m_service == Service::Logind )
{
inhibitCall = QDBusMessage::createMethodCall( QStringLiteral( "org.freedesktop.login1" ),
QStringLiteral( "/org/freedesktop/login1" ),
QStringLiteral( "org.freedesktop.login1.Manager" ),
QStringLiteral( "Inhibit" ) );
}
else if ( m_service == Service::ConsoleKit )
{
inhibitCall = QDBusMessage::createMethodCall( QStringLiteral( "org.freedesktop.ConsoleKit" ),
QStringLiteral( "/org/freedesktop/ConsoleKit/Manager" ),
QStringLiteral( "org.freedesktop.ConsoleKit.Manager" ),
QStringLiteral( "Inhibit" ) );
}
else
{
cError() << "System sleep interface not supported.";
return;
}
inhibitCall.setArguments(
{ { "sleep:shutdown" }, { tr( "Calamares" ) }, { tr( "Installation in progress", "@status" ) }, { "block" } } );
auto asyncReply = systemBus.asyncCall( inhibitCall );
auto* replyWatcher = new QDBusPendingCallWatcher( asyncReply, this );
QObject::connect(
replyWatcher, &QDBusPendingCallWatcher::finished, this, &LoginManagerInterface::inhibitDBusCallFinished );
}
void
LoginManagerInterface::uninhibitSleep()
{
if ( m_inhibitFd == -1 )
{
cDebug() << "Sleep was never inhibited.";
this->deleteLater();
return;
}
if ( close( m_inhibitFd ) != 0 )
{
cError() << "Could not uninhibit sleep:" << strerror( errno );
}
this->deleteLater();
}
} // namespace } // namespace
namespace Calamares namespace Calamares
{ {
SleepInhibitor::SleepInhibitor() SleepInhibitor::SleepInhibitor()
{ {
// Create a PowerManagementInterface object with intentionally no parent // Create a LoginManagerInterface object with intentionally no parent
// so it is not destroyed along with this. Instead, when this // so it is not destroyed along with this. Instead, when this
// is destroyed, **start** the uninhibit-sleep call which will (later) // is destroyed, **start** the uninhibit-sleep call which will (later)
// destroy the PowerManagementInterface object. // destroy the LoginManagerInterface object.
if ( auto* l = LoginManagerInterface::makeForRegisteredService( nullptr ) )
{
l->inhibitSleep();
connect( this, &QObject::destroyed, l, &LoginManagerInterface::uninhibitSleep );
}
// If no login manager service was present, try the same thing
// with PowerManagementInterface.
else
{
auto* p = new PowerManagementInterface( nullptr ); auto* p = new PowerManagementInterface( nullptr );
p->inhibitSleep(); p->inhibitSleep();
connect( this, &QObject::destroyed, p, &PowerManagementInterface::uninhibitSleep ); connect( this, &QObject::destroyed, p, &PowerManagementInterface::uninhibitSleep );
} }
}
SleepInhibitor::~SleepInhibitor() = default; SleepInhibitor::~SleepInhibitor() = default;