diff --git a/src/libcalamares/JobQueue.cpp b/src/libcalamares/JobQueue.cpp index d35ae4514..002899220 100644 --- a/src/libcalamares/JobQueue.cpp +++ b/src/libcalamares/JobQueue.cpp @@ -17,6 +17,7 @@ #include "utils/Logger.h" #include +#include #include #include #include @@ -24,6 +25,8 @@ #include #include +#include +#include // 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;