[libcalamares] Block sleep with systemd/CK login manager if present
Calamares running as root is not always conducive to successfully communicating with the fd.o PowerManagement interface on the user session bus. The login manager interface has long provided a similarly capable sleep inhibition functionality, which at this point would be present on the vast majority of distributions. Use it if systemd-logind or ConsoleKit2 have registered this service on the system bus. Regardless of running as root or user, Calamares shouldn't have an issue contacting this interface. Fixes #2384
This commit is contained in:
parent
b5533caece
commit
37179801eb
@ -17,6 +17,7 @@
|
||||
#include "utils/Logger.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusConnectionInterface>
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusPendingCall>
|
||||
#include <QDBusPendingReply>
|
||||
@ -24,6 +25,8 @@
|
||||
#include <QThread>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unistd.h> // for close()
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -36,7 +39,7 @@ namespace
|
||||
// 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()
|
||||
* to (try to) stop system sleep / suspend. Call uninhibitSleep()
|
||||
@ -166,19 +169,165 @@ PowerManagementInterface::uninhibitSleep()
|
||||
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 Calamares
|
||||
{
|
||||
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
|
||||
// is destroyed, **start** the uninhibit-sleep call which will (later)
|
||||
// destroy the PowerManagementInterface object.
|
||||
auto* p = new PowerManagementInterface( nullptr );
|
||||
p->inhibitSleep();
|
||||
connect( this, &QObject::destroyed, p, &PowerManagementInterface::uninhibitSleep );
|
||||
// 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 );
|
||||
p->inhibitSleep();
|
||||
connect( this, &QObject::destroyed, p, &PowerManagementInterface::uninhibitSleep );
|
||||
}
|
||||
}
|
||||
|
||||
SleepInhibitor::~SleepInhibitor() = default;
|
||||
|
Loading…
Reference in New Issue
Block a user