From 409442c1ea8f167485642fbdb3494ff58900966f Mon Sep 17 00:00:00 2001 From: Michele Calgaro Date: Sat, 21 Jun 2025 21:49:09 +0900 Subject: [PATCH] kdesktop: fix deadlock condition between kdesktop and kdesktop lock. The logic to handle communication with kdesktop_lock is now running completely in a separate thread and event loop, meaning the main GUI thread remains responsive all the time and can handle interaction with X11, DCOP and DBUS calls. This resolves issue #589. The commit also solves the first problem reported in issue #640 and loosely related to PR #526. Signed-off-by: Michele Calgaro --- kdesktop/DESIGN | 13 + kdesktop/KScreensaverIface.h | 18 +- kdesktop/lock/lockprocess.h | 3 - kdesktop/lockeng.cpp | 1144 ++++++++++++++++++---------------- kdesktop/lockeng.h | 162 ++--- kdesktop/main.cpp | 18 +- 6 files changed, 721 insertions(+), 637 deletions(-) diff --git a/kdesktop/DESIGN b/kdesktop/DESIGN index 7e195378f..170a86a75 100644 --- a/kdesktop/DESIGN +++ b/kdesktop/DESIGN @@ -124,3 +124,16 @@ kdesktop_lock to kdesktop communication: - TTIN: the lock process is ready. This is sent after the process has been created/respawned - USR2: the lock/screensaver has been activated - USR1: the lock/screensaver has been unlocked/stopped + +Communication is handled by the screen saver engine defined in 'lockeng.{h,cpp}'. +The engine is split into two parts, the 'SaverEngine' running in the GUI thread and +the 'SaverEngineEventHandler' running in a separate thread and eventloop. +The 'SaverEngine' handles communication with X11, DCOP and DBUS while the +'SaverEngineEventHandler' handles communication with the actual lock process. +Several actions require cooperation of the two parts, so in various methods +there will be inter-thread calls (using timers or by emitting signals) to +trigger the other side remaining logic. +This complex design is necessary to avoid blocking the main GUI application event loop, +which has several tasks to manage and therefore can't affort to wait in a suspended state. +This was previously leading to deadlock when DCOP calls where executed on the secondary +thread/eventloop, for example when changing desktop while the lock process was restarting. diff --git a/kdesktop/KScreensaverIface.h b/kdesktop/KScreensaverIface.h index de5c19f2b..b6434738e 100644 --- a/kdesktop/KScreensaverIface.h +++ b/kdesktop/KScreensaverIface.h @@ -12,11 +12,10 @@ public: KScreensaverIface() : DCOPObject("KScreensaverIface") {} k_dcop: - /** Lock the screen now even if the screensaver does not lock by default. */ + /** Lock the screen now even if the screensaver does not lock by default */ virtual void lock() = 0; - /** Save the screen now. If the user has locking enabled, the screen is - * locked also. */ + /** Start the screensaver now. If the user has locking enabled, the screen is locked also */ virtual void save() = 0; /** Quit the screensaver if it is running */ @@ -29,22 +28,23 @@ k_dcop: * Enable/disable the screensaver * returns true if the action succeeded */ - virtual bool enable( bool e ) = 0; + virtual bool enable(bool e) = 0; /** Is the screen currently blanked? */ virtual bool isBlanked() = 0; - /** Reload the screensaver configuration. */ + /** Reload the screensaver configuration */ virtual void configure() = 0; - /** Only blank the screen (and possibly lock). Do not use a custom - * screen saver in the interest of saving battery. + /** + * Set the screensaver to blank (and possibly lock). + * This method does not actually start the screensaver. */ - virtual void setBlankOnly( bool blankOnly ) = 0; + virtual void setBlankOnly(bool blankOnly) = 0; /*** * @internal - */ + */ virtual void saverLockReady() = 0; }; diff --git a/kdesktop/lock/lockprocess.h b/kdesktop/lock/lockprocess.h index 5a6a3b4d4..1afc9bb46 100644 --- a/kdesktop/lock/lockprocess.h +++ b/kdesktop/lock/lockprocess.h @@ -106,9 +106,6 @@ class LockProcess : public TQWidget TDECryptographicCardDevice* cryptographicCardDevice(); - signals: - void terminateHelperThread(); - public slots: void quitSaver(); void preparePopup(); diff --git a/kdesktop/lockeng.cpp b/kdesktop/lockeng.cpp index c4b781c2a..7216acc4f 100644 --- a/kdesktop/lockeng.cpp +++ b/kdesktop/lockeng.cpp @@ -6,7 +6,6 @@ // Copyright (c) 2012 Timothy Pearson // - #include #include @@ -45,52 +44,49 @@ #include "lockeng.moc" #include "kdesktopsettings.h" -#define SYSTEMD_LOGIN1_SERVICE "org.freedesktop.login1" -#define SYSTEMD_LOGIN1_PATH "/org/freedesktop/login1" -#define SYSTEMD_LOGIN1_MANAGER_IFACE "org.freedesktop.login1.Manager" -#define SYSTEMD_LOGIN1_SESSION_IFACE "org.freedesktop.login1.Session" -#define SYSTEMD_LOGIN1_SEAT_IFACE "org.freedesktop.login1.Seat" +#include "xautolock_c.h" + +#define SYSTEMD_LOGIN1_SERVICE "org.freedesktop.login1" +#define SYSTEMD_LOGIN1_MANAGER_IFACE "org.freedesktop.login1.Manager" +#define SYSTEMD_LOGIN1_SESSION_IFACE "org.freedesktop.login1.Session" +#define SYSTEMD_LOGIN1_SEAT_IFACE "org.freedesktop.login1.Seat" +#define SYSTEMD_LOGIN1_PATH "/org/freedesktop/login1" #define DBUS_CONN_NAME "kdesktop_lock" -#include "xautolock_c.h" extern xautolock_corner_t xautolock_corners[ 4 ]; - bool trinity_lockeng_sak_available = true; -SaverEngine* m_masterSaverEngine = NULL; +SaverEngineEventHandler *gbl_saverEngineEventHandler = nullptr; + static void sigusr1_handler(int) { - if (m_masterSaverEngine) { - m_masterSaverEngine->m_threadHelperObject->slotLockProcessWaiting(); + if (gbl_saverEngineEventHandler) + { + gbl_saverEngineEventHandler->lockProcessExited(); } } + static void sigusr2_handler(int) { - if (m_masterSaverEngine) { - m_masterSaverEngine->m_threadHelperObject->slotLockProcessFullyActivated(); + if (gbl_saverEngineEventHandler) + { + gbl_saverEngineEventHandler->lockProcessFullyActivated(); } } + static void sigttin_handler(int) { - if (m_masterSaverEngine) { - m_masterSaverEngine->slotLockProcessReady(); + if (gbl_saverEngineEventHandler) + { + gbl_saverEngineEventHandler->lockProcessReady(); } } -//=========================================================================== -// -// Screen saver engine. Doesn't handle the actual screensaver window, -// starting screensaver hacks, or password entry. That's done by -// a newly started process. -// SaverEngine::SaverEngine() - : TQWidget(), + : TQObject(), KScreensaverIface(), mBlankOnly(false), - mSAKProcess(NULL), - mTerminationRequested(false), - mSaverProcessReady(false), mNewVTAfterLockEngage(false), mValidCryptoCardInserted(false), mSwitchVTAfterLockEngage(-1), @@ -99,7 +95,6 @@ SaverEngine::SaverEngine() systemdSession(0) { // handle SIGUSR1 - m_masterSaverEngine = this; mSignalAction.sa_handler= sigusr1_handler; sigemptyset(&(mSignalAction.sa_mask)); sigaddset(&(mSignalAction.sa_mask), SIGUSR1); @@ -107,7 +102,6 @@ SaverEngine::SaverEngine() sigaction(SIGUSR1, &mSignalAction, 0L); // handle SIGUSR2 - m_masterSaverEngine = this; mSignalAction.sa_handler= sigusr2_handler; sigemptyset(&(mSignalAction.sa_mask)); sigaddset(&(mSignalAction.sa_mask), SIGUSR2); @@ -115,7 +109,6 @@ SaverEngine::SaverEngine() sigaction(SIGUSR2, &mSignalAction, 0L); // handle SIGTTIN - m_masterSaverEngine = this; mSignalAction.sa_handler= sigttin_handler; sigemptyset(&(mSignalAction.sa_mask)); sigaddset(&(mSignalAction.sa_mask), SIGTTIN); @@ -123,92 +116,52 @@ SaverEngine::SaverEngine() sigaction(SIGTTIN, &mSignalAction, 0L); // Save X screensaver parameters - XGetScreenSaver(tqt_xdisplay(), &mXTimeout, &mXInterval, - &mXBlanking, &mXExposures); - - mState = Waiting; - mXAutoLock = 0; + XGetScreenSaver(tqt_xdisplay(), &mXTimeout, &mXInterval, &mXBlanking, &mXExposures); + + // Create event handler thread, event loop and object + m_eventHandlerThread = new TQEventLoopThread; + m_eventHandlerThread->start(); + m_saverEngineEventHandler = new SaverEngineEventHandler(this); + gbl_saverEngineEventHandler = m_saverEngineEventHandler; + m_saverEngineEventHandler->moveToThread(m_eventHandlerThread); + connect(this, TQ_SIGNAL(terminateEventHandlerThread()), m_saverEngineEventHandler, TQ_SLOT(terminateThread())); + connect(this, TQ_SIGNAL(lockScreenSignal(bool)), m_saverEngineEventHandler, TQ_SLOT(lockScreen(bool))); + connect(this, TQ_SIGNAL(activateSaverOrLockSignal(LockType)), + m_saverEngineEventHandler, TQ_SLOT(activateSaverOrLock(LockType))); + + mXAutoLock = nullptr; mEnabled = false; - m_helperThread = new TQEventLoopThread; - m_helperThread->start(); - m_threadHelperObject = new SaverEngineThreadHelperObject; - m_threadHelperObject->moveToThread(m_helperThread); - connect(this, TQ_SIGNAL(terminateHelperThread()), m_threadHelperObject, TQ_SLOT(terminateThread())); - connect(m_threadHelperObject, TQ_SIGNAL(lockProcessWaiting()), this, TQ_SLOT(lockProcessWaiting())); - connect(m_threadHelperObject, TQ_SIGNAL(lockProcessFullyActivated()), this, TQ_SLOT(lockProcessFullyActivated())); - - connect(&mLockProcess, TQ_SIGNAL(processExited(TDEProcess *)), - TQ_SLOT(lockProcessExited())); - configure(); - // Create SAK process only if SAK is enabled - KSimpleConfig *config; - struct stat st; - if (stat( KDE_CONFDIR "/tdm/tdmdistrc" , &st) == 0) { - config = new KSimpleConfig( TQString::fromLatin1( KDE_CONFDIR "/tdm/tdmdistrc" )); - } - else { - config = new KSimpleConfig( TQString::fromLatin1( KDE_CONFDIR "/tdm/tdmrc" )); - } - config->setGroup("X-:*-Greeter"); - bool useSAKProcess = false; -#ifdef BUILD_TSAK - useSAKProcess = config->readBoolEntry("UseSAK", false) && KDesktopSettings::useTDESAK(); -#endif - if (useSAKProcess) { - mSAKProcess = new TDEProcess; - *mSAKProcess << "tdmtsak"; - connect(mSAKProcess, TQ_SIGNAL(processExited(TDEProcess*)), this, TQ_SLOT(slotSAKProcessExited())); - TQTimer::singleShot( 0, this, TQ_SLOT(handleSecureDialog()) ); - } - - mLockProcess.clearArguments(); - TQString path = TDEStandardDirs::findExe( "kdesktop_lock" ); - if( path.isEmpty()) - { - kdDebug( 1204 ) << "Can't find kdesktop_lock!" << endl; - } - mLockProcess << path; - mLockProcess << TQString( "--internal" ) << TQString( "%1" ).arg(getpid()); - if (!mLockProcess.start()) - { - kdDebug( 1204 ) << "Failed to start kdesktop_lock!" << endl; - } - - // Prevent kdesktop_lock signals from being handled by the wrong (GUI) thread - sigemptyset(&mThreadBlockSet); - sigaddset(&mThreadBlockSet, SIGUSR1); - sigaddset(&mThreadBlockSet, SIGUSR2); - sigaddset(&mThreadBlockSet, SIGTTIN); - pthread_sigmask(SIG_BLOCK, &mThreadBlockSet, NULL); - - // Wait for the saver process to signal ready... - if (!waitForLockProcessStart()) { - kdDebug( 1204 ) << "Failed to initialize kdesktop_lock (unexpected termination)!" << endl; - } - - // lock the desktop if required - config->setGroup("X-:0-Core"); - bool autoLoginEnable = config->readBoolEntry("AutoLoginEnable", false); - bool autoLoginLocked = config->readBoolEntry("AutoLoginLocked", false); - if (autoLoginEnable && autoLoginLocked) { - mLockProcess.kill(SIGTTOU); - mLockProcess.kill(SIGUSR1); - } - delete config; - config = NULL; + // Prevent kdesktop_lock signals from being handled by the main GUI thread. + // Those signals will be handled by m_eventHandlerThread instead + // + // Make sure to keep this code after the constructor of `m_eventHandlerThread`, so that + // the new thread starts with the signals unblocked. + sigset_t sigBlockMask; + sigemptyset(&sigBlockMask); + sigaddset(&sigBlockMask, SIGUSR1); + sigaddset(&sigBlockMask, SIGUSR2); + sigaddset(&sigBlockMask, SIGTTIN); + sigaddset(&sigBlockMask, SIGCHLD); + pthread_sigmask(SIG_BLOCK, &sigBlockMask, NULL); + + // Start SAK and lock processes + TQTimer::singleShot(0, m_saverEngineEventHandler, TQ_SLOT(restartLockProcess())); #ifdef WITH_TDEHWLIB // Initialize SmartCard readers TDEGenericDevice *hwdevice; TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices(); TDEGenericHardwareList cardReaderList = hwdevices->listByDeviceClass(TDEGenericDeviceType::CryptographicCard); - for (hwdevice = cardReaderList.first(); hwdevice; hwdevice = cardReaderList.next()) { - TDECryptographicCardDevice* cdevice = static_cast(hwdevice); - connect(cdevice, TQ_SIGNAL(certificateListAvailable(TDECryptographicCardDevice*)), this, TQ_SLOT(cryptographicCardInserted(TDECryptographicCardDevice*))); - connect(cdevice, TQ_SIGNAL(cardRemoved(TDECryptographicCardDevice*)), this, TQ_SLOT(cryptographicCardRemoved(TDECryptographicCardDevice*))); + for (hwdevice = cardReaderList.first(); hwdevice; hwdevice = cardReaderList.next()) + { + TDECryptographicCardDevice *cdevice = static_cast(hwdevice); + connect(cdevice, TQ_SIGNAL(certificateListAvailable(TDECryptographicCardDevice*)), + this, TQ_SLOT(cryptographicCardInserted(TDECryptographicCardDevice*))); + connect(cdevice, TQ_SIGNAL(cardRemoved(TDECryptographicCardDevice*)), + this, TQ_SLOT(cryptographicCardRemoved(TDECryptographicCardDevice*))); cdevice->enableCardMonitoring(true); } @@ -216,9 +169,11 @@ SaverEngine::SaverEngine() KUser userinfo; TQString fileName = userinfo.homeDir() + "/.tde_card_login_state"; TQFile flagFile(fileName); - if (flagFile.open(IO_ReadOnly)) { + if (flagFile.open(IO_ReadOnly)) + { TQTextStream stream(&flagFile); - if (stream.readLine().startsWith("1")) { + if (stream.readLine().startsWith("1")) + { // Card was likely used to log in TQTimer::singleShot(5000, this, TQ_SLOT(cardStartupTimeout())); } @@ -229,70 +184,66 @@ SaverEngine::SaverEngine() dBusConnect(); } -//--------------------------------------------------------------------------- -// -// Destructor - usual cleanups. -// SaverEngine::~SaverEngine() { - if (mState == Waiting) { - kill(mLockProcess.pid(), SIGKILL); - } - - mLockProcess.detach(); // don't kill it if we crash + m_saverEngineEventHandler->terminateLockProcess(); delete mXAutoLock; - dBusClose(); // Restore X screensaver parameters - XSetScreenSaver(tqt_xdisplay(), mXTimeout, mXInterval, mXBlanking, - mXExposures); - - terminateHelperThread(); - m_helperThread->wait(); - delete m_threadHelperObject; - delete m_helperThread; + XSetScreenSaver(tqt_xdisplay(), mXTimeout, mXInterval, mXBlanking, mXExposures); + emit terminateEventHandlerThread(); + m_eventHandlerThread->wait(); + delete m_saverEngineEventHandler; + delete m_eventHandlerThread; } -void SaverEngine::cardStartupTimeout() { - if (!mValidCryptoCardInserted) { - // Restore saver timeout - configure(); - - // Force lock - lockScreen(); +void SaverEngine::cardStartupTimeout() +{ + if (!mValidCryptoCardInserted) + { + configure(); // Restore saver timeout + lockScreen(); // Force lock } } -void SaverEngine::cryptographicCardInserted(TDECryptographicCardDevice* cdevice) { +void SaverEngine::cryptographicCardInserted(TDECryptographicCardDevice* cdevice) +{ #ifdef WITH_TDEHWLIB TQString login_name = TQString::null; X509CertificatePtrList certList = cdevice->cardX509Certificates(); - if (certList.count() > 0) { + if (certList.count() > 0) + { KSSLCertificate* card_cert = NULL; card_cert = KSSLCertificate::fromX509(certList[0]); TQStringList cert_subject_parts = TQStringList::split("/", card_cert->getSubject(), false); - for (TQStringList::Iterator it = cert_subject_parts.begin(); it != cert_subject_parts.end(); ++it ) { + for (TQStringList::Iterator it = cert_subject_parts.begin(); it != cert_subject_parts.end(); ++it) + { TQString lcpart = (*it).lower(); - if (lcpart.startsWith("cn=")) { + if (lcpart.startsWith("cn=")) + { login_name = lcpart.right(lcpart.length() - strlen("cn=")); } } delete card_cert; } - if (login_name != "") { + if (login_name != "") + { KUser user; - if (login_name == user.loginName()) { + if (login_name == user.loginName()) + { mValidCryptoCardInserted = true; } } #endif } -void SaverEngine::cryptographicCardRemoved(TDECryptographicCardDevice* cdevice) { +void SaverEngine::cryptographicCardRemoved(TDECryptographicCardDevice* cdevice) +{ #ifdef WITH_TDEHWLIB - if (mValidCryptoCardInserted) { + if (mValidCryptoCardInserted) + { mValidCryptoCardInserted = false; // Restore saver timeout @@ -304,226 +255,166 @@ void SaverEngine::cryptographicCardRemoved(TDECryptographicCardDevice* cdevice) #endif } -//--------------------------------------------------------------------------- -// -// This should be called only using DCOP. -// +// DCOP interface method void SaverEngine::lock() { lockScreen(true); } -//--------------------------------------------------------------------------- -// -// Lock the screen -// -void SaverEngine::lockScreen(bool DCOP) +void SaverEngine::lockScreen(bool dcop) { - if (mValidCryptoCardInserted) { + if (mValidCryptoCardInserted) + { + kdDebug(1204) << "SaverEngine: crypto card inserted, ignore lock request" << endl; return; } + emit lockScreenSignal(dcop); +} - bool ok = true; - if (mState == Waiting) - { - ok = startLockProcess( ForceLock ); - // It takes a while for kdesktop_lock to start and lock the screen. - // Therefore delay the DCOP call until it tells kdesktop that the locking is in effect. - // This is done only for --forcelock . - if( ok && mState != Saving ) - { - if (DCOP) { - DCOPClientTransaction* trans = tdeApp->dcopClient()->beginTransaction(); - if (trans) { - mLockTransactions.append( trans ); - } - } - } - } - else +void SaverEngine::lockScreenGUI() +{ + DCOPClientTransaction *trans = tdeApp->dcopClient()->beginTransaction(); + if (trans) { - mLockProcess.kill( SIGHUP ); + mLockTransactions.append(trans); } } void SaverEngine::processLockTransactions() { - for( TQValueVector< DCOPClientTransaction* >::ConstIterator it = mLockTransactions.begin(); - it != mLockTransactions.end(); - ++it ) + TQValueVector::ConstIterator it = mLockTransactions.begin(); + for (; it != mLockTransactions.end(); ++it) { TQCString replyType = "void"; TQByteArray arr; - tdeApp->dcopClient()->endTransaction( *it, replyType, arr ); + tdeApp->dcopClient()->endTransaction(*it, replyType, arr); } mLockTransactions.clear(); } void SaverEngine::saverLockReady() { - if( mState != Engaging ) + if (m_saverEngineEventHandler->getState() != SaverState::Engaging) { - kdDebug( 1204 ) << "Got unexpected saverReady()" << endl; + kdDebug(1204) << "Got unexpected saverLockReady()" << endl; } - kdDebug( 1204 ) << "Saver Lock Ready" << endl; + + kdDebug(1204) << "Saver Lock Ready" << endl; processLockTransactions(); } -//--------------------------------------------------------------------------- +// DCOP interface method void SaverEngine::save() { - if (!mValidCryptoCardInserted) { - if (mState == Waiting) { - startLockProcess( DefaultLock ); - } + if (mValidCryptoCardInserted) + { + kdDebug(1204) << "SaverEngine: crypto card inserted, ignore save request" << endl; + return; } + TQTimer::singleShot(0, m_saverEngineEventHandler, TQ_SLOT(saveScreen())); } -//--------------------------------------------------------------------------- +// DCOP interface method void SaverEngine::quit() { - if (mState == Saving || mState == Engaging) - { - stopLockProcess(); - } + TQTimer::singleShot(0, m_saverEngineEventHandler, TQ_SLOT(stopLockProcess())); } -//--------------------------------------------------------------------------- +// DCOP interface method bool SaverEngine::isEnabled() { return mEnabled; } -//--------------------------------------------------------------------------- -bool SaverEngine::enable( bool e ) +// DCOP interface method +bool SaverEngine::enable(bool e) { - if ( e == mEnabled ) - return true; + if (e == mEnabled) + return true; // If we aren't in a suitable state, we will not reconfigure. - if (mState != Waiting) + if (m_saverEngineEventHandler->getState() != SaverState::Waiting) return false; mEnabled = e; - if (mEnabled) { - if ( !mXAutoLock ) { + if (mEnabled) + { + if (!mXAutoLock) + { mXAutoLock = new XAutoLock(); connect(mXAutoLock, TQ_SIGNAL(timeout()), TQ_SLOT(idleTimeout())); } mXAutoLock->setTimeout(mTimeout); mXAutoLock->setDPMS(true); - //mXAutoLock->changeCornerLockStatus( mLockCornerTopLeft, mLockCornerTopRight, mLockCornerBottomLeft, mLockCornerBottomRight); // We'll handle blanking XSetScreenSaver(tqt_xdisplay(), mTimeout + 10, mXInterval, PreferBlanking, mXExposures); - kdDebug() << "XSetScreenSaver " << mTimeout + 10 << endl; - mXAutoLock->start(); - - kdDebug(1204) << "Saver Engine started, timeout: " << mTimeout << endl; + kdDebug(1204) << "Saver engine started, timeout: " << mTimeout << endl; } - else { - if (mXAutoLock) { + else + { + if (mXAutoLock) + { delete mXAutoLock; - mXAutoLock = 0; + mXAutoLock = nullptr; } - XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset ); + XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset); XSetScreenSaver(tqt_xdisplay(), 0, mXInterval, PreferBlanking, DontAllowExposures); - kdDebug(1204) << "Saver Engine disabled" << endl; + kdDebug(1204) << "Saver engine disabled" << endl; } return true; } -//--------------------------------------------------------------------------- +// DCOP interface method bool SaverEngine::isBlanked() { - return (mState != Waiting); + return (m_saverEngineEventHandler->getState() != SaverState::Waiting); } void SaverEngine::enableExports() { #ifdef TQ_WS_X11 - kdDebug(270) << k_lineinfo << "activating background exports.\n"; + kdDebug(270) << k_lineinfo << "activating background exports" << endl; DCOPClient *client = tdeApp->dcopClient(); - if (!client->isAttached()) { + if (!client->isAttached()) + { client->attach(); } TQByteArray data; - TQDataStream args( data, IO_WriteOnly ); + TQDataStream args(data, IO_WriteOnly); args << 1; - TQCString appname( "kdesktop" ); + TQCString appname("kdesktop"); int screen_number = DefaultScreen(tqt_xdisplay()); - if ( screen_number ) { - appname.sprintf("kdesktop-screen-%d", screen_number ); + if (screen_number) + { + appname.sprintf("kdesktop-screen-%d", screen_number); } - client->send( appname, "KBackgroundIface", "setExport(int)", data ); + client->send(appname, "KBackgroundIface", "setExport(int)", data); #endif } -//--------------------------------------------------------------------------- -void SaverEngine::handleSecureDialog() -{ - // Wait for SAK press - if (mSAKProcess && !mSAKProcess->isRunning()) { - mSAKProcess->start(); - } -} - -void SaverEngine::slotSAKProcessExited() -{ - if (!mSAKProcess) { - printf("[kdesktop] SAK process does not exist. Something went wrong. Ignoring...\n"); fflush(stdout); - return; - } - int retcode = mSAKProcess->exitStatus(); - if ((retcode != 0) && (mSAKProcess->normalExit())) { - trinity_lockeng_sak_available = false; - printf("[kdesktop] SAK driven secure dialog is not available for use (retcode %d). Check tdmtsak for proper functionality.\n", retcode); fflush(stdout); - } - - if (mState == Preparing) { - return; - } - - if (mSAKProcess->normalExit() && trinity_lockeng_sak_available) { - bool ok = true; - if (mState == Waiting) - { - ok = startLockProcess( SecureDialog ); - if( ok && mState != Saving ) - { - } - } - else - { - mLockProcess.kill( SIGHUP ); - } - } -} - -//--------------------------------------------------------------------------- -// // Read and apply configuration. -// void SaverEngine::configure() { // If we aren't in a suitable state, we will not reconfigure. - if (mState != Waiting) { + if (m_saverEngineEventHandler->getState() != SaverState::Waiting) + { return; } // create a new config obj to ensure we read the latest options KDesktopSettings::self()->readConfig(); - bool e = KDesktopSettings::screenSaverEnabled(); mTimeout = KDesktopSettings::timeout(); - - mEnabled = !e; // force the enable() + bool e = KDesktopSettings::screenSaverEnabled(); + mEnabled = !e; // enable the screensaver by forcibly toggling it + enable(e); int action; action = KDesktopSettings::actionTopLeft(); @@ -534,320 +425,183 @@ void SaverEngine::configure() xautolock_corners[2] = applyManualSettings(action); action = KDesktopSettings::actionBottomRight(); xautolock_corners[3] = applyManualSettings(action); - - enable( e ); } -//--------------------------------------------------------------------------- -// -// Set a variable to indicate only using the blanker and not the saver. -// -void SaverEngine::setBlankOnly( bool blankOnly ) +// DCOP interface method +// Set a variable to indicate only to blank the screen and not use the saver +void SaverEngine::setBlankOnly(bool blankOnly) { mBlankOnly = blankOnly; - // FIXME: if running, stop and restart? What about security - // implications of this? } -bool SaverEngine::restartDesktopLockProcess() +void SaverEngine::activateSaverOrLockGUI() { - if (!mLockProcess.isRunning()) { - mSaverProcessReady = false; - mLockProcess.clearArguments(); - TQString path = TDEStandardDirs::findExe( "kdesktop_lock" ); - if (path.isEmpty()) { - kdDebug( 1204 ) << "Can't find kdesktop_lock!" << endl; - return false; - } - mLockProcess << path; - mLockProcess << TQString( "--internal" ) << TQString( "%1" ).arg(getpid()); - if (!mLockProcess.start()) { - kdDebug( 1204 ) << "Failed to start kdesktop_lock!" << endl; - return false; - } - // Wait for the saver process to signal ready... - if (!waitForLockProcessStart()) { - kdDebug( 1204 ) << "Failed to initialize kdesktop_lock (unexpected termination)!" << endl; - return false; - } - } - return true; -} - -//--------------------------------------------------------------------------- -// -// Start the screen saver. -// -bool SaverEngine::startLockProcess( LockType lock_type ) -{ - int ret; - - if (mState == Saving) { - return true; - } - - mState = Preparing; - if (mSAKProcess) { - mSAKProcess->kill(SIGTERM); - } - - enableExports(); - - kdDebug(1204) << "SaverEngine: starting saver" << endl; - emitDCOPSignal("KDE_start_screensaver()", TQByteArray()); - - if (!restartDesktopLockProcess()) { - mState = Waiting; - return false; - } - - switch( lock_type ) - { - case ForceLock: - mLockProcess.kill(SIGUSR1); // Request forcelock - break; - case DontLock: - mLockProcess.kill(SIGUSR2); // Request dontlock - break; - case SecureDialog: - mLockProcess.kill(SIGWINCH); // Request secure dialog - break; - default: - break; - } - if (mBlankOnly) { - mLockProcess.kill(SIGTTIN); // Request blanking - } - - ret = mLockProcess.kill(SIGTTOU); // Start lock - if (!ret) { - mState = Waiting; - return false; - } XSetScreenSaver(tqt_xdisplay(), 0, mXInterval, PreferBlanking, mXExposures); - - mState = Engaging; - if (mXAutoLock) { + if (mXAutoLock) + { mXAutoLock->stop(); } - return true; + emitDCOPSignal("KDE_start_screensaver()", TQByteArray()); } -//--------------------------------------------------------------------------- -// -// Stop the screen saver. -// -void SaverEngine::stopLockProcess() +void SaverEngine::stopLockProcessGUI() { - if (mState == Waiting) { - kdWarning(1204) << "SaverEngine::stopSaver() saver not active" << endl; - return; - } - kdDebug(1204) << "SaverEngine: stopping lock" << endl; emitDCOPSignal("KDE_stop_screensaver()", TQByteArray()); - mTerminationRequested = true; - mLockProcess.kill(); - - if (mEnabled) { - if (mXAutoLock) { + if (mEnabled) + { + if (mXAutoLock) + { mXAutoLock->start(); } - XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset ); + XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset); XSetScreenSaver(tqt_xdisplay(), mTimeout + 10, mXInterval, PreferBlanking, mXExposures); } processLockTransactions(); - mState = Waiting; - if( systemdSession && systemdSession->canSend() ) { + if (systemdSession && systemdSession->canSend()) + { TQValueList params; params << TQT_DBusData::fromBool(false); TQT_DBusMessage reply = systemdSession->sendWithReply("SetIdleHint", params); } } -void SaverEngine::recoverFromHackingAttempt() +void SaverEngine::terminateTDESession() { - // Try to relaunch saver with forcelock - if (!startLockProcess(ForceLock)) { - // Terminate the TDE session ASAP! - // Values are explained at http://lists.kde.org/?l=kde-linux&m=115770988603387 - TQByteArray data; - TQDataStream arg(data, IO_WriteOnly); - arg << (int)0 << (int)0 << (int)2; - if (!tdeApp->dcopClient()->send("ksmserver", "default", "logout(int,int,int)", data)) { - // Someone got to DCOP before we did - // Try an emergency system logout - system("logout"); - } - } -} - -void SaverEngine::lockProcessExited() -{ - bool abnormalExit = false; - if (!mLockProcess.normalExit()) { - abnormalExit = true; - } - else { - if (mLockProcess.exitStatus() != 0) { - abnormalExit = true; - } - } - if (mTerminationRequested) { - abnormalExit = false; - mTerminationRequested = false; - } - if (abnormalExit) { - // PROBABLE HACKING ATTEMPT DETECTED - restartDesktopLockProcess(); - mState = Waiting; - TQTimer::singleShot( 100, this, TQ_SLOT(recoverFromHackingAttempt()) ); - } - else { - // Restart the lock process - restartDesktopLockProcess(); + // Terminate the TDE session ASAP! + // Values are explained at http://lists.kde.org/?l=kde-linux&m=115770988603387 + TQByteArray data; + TQDataStream arg(data, IO_WriteOnly); + arg << (int)0 << (int)0 << (int)2; + if (!tdeApp->dcopClient()->send("ksmserver", "default", "logout(int,int,int)", data)) + { + // Someone got to DCOP before we did. Try an emergency system logout + system("logout"); } } -void SaverEngineThreadHelperObject::slotLockProcessWaiting() +void SaverEngine::lockProcessFullyActivatedGUI() { - // lockProcessWaiting cannot be called directly from a signal handler, as it will hang in certain obscure circumstances - // Instead we use a single-shot timer to immediately call lockProcessWaiting once control has returned to the Qt main loop - lockProcessWaiting(); -} - -void SaverEngineThreadHelperObject::slotLockProcessFullyActivated() -{ - lockProcessFullyActivated(); -} - -void SaverEngine::lockProcessFullyActivated() -{ - mState = Saving; - - if( systemdSession && systemdSession->canSend() ) { + if (systemdSession && systemdSession->canSend()) + { TQValueList params; params << TQT_DBusData::fromBool(true); TQT_DBusMessage reply = systemdSession->sendWithReply("SetIdleHint", params); } - if (mNewVTAfterLockEngage) { + if (mNewVTAfterLockEngage) + { DM().startReserve(); mNewVTAfterLockEngage = false; } - else if (mSwitchVTAfterLockEngage != -1) { + else if (mSwitchVTAfterLockEngage != -1) + { DM().switchVT(mSwitchVTAfterLockEngage); mSwitchVTAfterLockEngage = -1; } } -void SaverEngine::slotLockProcessReady() -{ - mSaverProcessReady = true; -} - -void SaverEngine::lockProcessWaiting() +void SaverEngine::lockProcessWaitingGUI() { - kdDebug(1204) << "SaverEngine: lock exited" << endl; - if (trinity_lockeng_sak_available) { - handleSecureDialog(); - } - if( mState == Waiting ) { - return; - } emitDCOPSignal("KDE_stop_screensaver()", TQByteArray()); - if (mEnabled) { - if (mXAutoLock) { + if (mEnabled) + { + if (mXAutoLock) + { mXAutoLock->start(); } - XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset ); + XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset); XSetScreenSaver(tqt_xdisplay(), mTimeout + 10, mXInterval, PreferBlanking, mXExposures); } processLockTransactions(); - mState = Waiting; - if( systemdSession && systemdSession->canSend() ) { + if (systemdSession && systemdSession->canSend()) + { TQValueList params; params << TQT_DBusData::fromBool(false); TQT_DBusMessage reply = systemdSession->sendWithReply("SetIdleHint", params); } } -//--------------------------------------------------------------------------- -// // XAutoLock has detected the required idle time. -// void SaverEngine::idleTimeout() { - if (!mValidCryptoCardInserted) { + if (!mValidCryptoCardInserted) + { // disable X screensaver - XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset ); + XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset); XSetScreenSaver(tqt_xdisplay(), 0, mXInterval, PreferBlanking, DontAllowExposures); - startLockProcess( DefaultLock ); + emit activateSaverOrLockSignal(DefaultLock); } } xautolock_corner_t SaverEngine::applyManualSettings(int action) { - if (action == 0) { + if (action == 0) + { kdDebug() << "no lock" << endl; return ca_nothing; } - else if (action == 1) { + else if (action == 1) + { kdDebug() << "lock screen" << endl; return ca_forceLock; } - else if (action == 2) { + else if (action == 2) + { kdDebug() << "prevent lock" << endl; return ca_dontLock; } - else{ + else + { kdDebug() << "no lock nothing" << endl; return ca_nothing; } } -/*! - * This function try a reconnect to D-Bus. +/* + * This function try to reconnect to D-Bus. * \return boolean with the result of the operation * \retval true if successful reconnected to D-Bus * \retval false if unsuccessful */ -bool SaverEngine::dBusReconnect() { - // close D-Bus connection - dBusClose(); - // init D-Bus conntection - return (dBusConnect()); -} +bool SaverEngine::dBusReconnect() +{ + dBusClose(); // close D-Bus connection + return (dBusConnect()); // init D-Bus conntection +} -/*! - * This function is used to close D-Bus connection. - */ -void SaverEngine::dBusClose() { - if( dBusConn.isConnected() ) { - if( dBusLocal ) { +// This function is used to close D-Bus connection. +void SaverEngine::dBusClose() +{ + if (dBusConn.isConnected()) + { + if (dBusLocal) + { delete dBusLocal; - dBusLocal = 0; + dBusLocal = nullptr; } - if( dBusWatch ) { + if (dBusWatch) + { delete dBusWatch; - dBusWatch = 0; + dBusWatch = nullptr; } - if( systemdSession ) { + if (systemdSession) + { delete systemdSession; - systemdSession = 0; + systemdSession = nullptr; } } dBusConn.closeConnection(DBUS_CONN_NAME); } -/*! - * This function is used to connect to D-Bus. - */ -bool SaverEngine::dBusConnect() { +// This function is used to connect to D-Bus. +bool SaverEngine::dBusConnect() +{ dBusConn = TQT_DBusConnection::addConnection(TQT_DBusConnection::SystemBus, DBUS_CONN_NAME); - if( !dBusConn.isConnected() ) { + if (!dBusConn.isConnected()) + { kdError() << "Failed to open connection to system message bus: " << dBusConn.lastError().message() << endl; TQTimer::singleShot(4000, this, TQ_SLOT(dBusReconnect())); return false; @@ -856,44 +610,49 @@ bool SaverEngine::dBusConnect() { // watcher for Disconnect signal dBusLocal = new TQT_DBusProxy(DBUS_SERVICE_DBUS, DBUS_PATH_LOCAL, DBUS_INTERFACE_LOCAL, dBusConn); TQObject::connect(dBusLocal, TQ_SIGNAL(dbusSignal(const TQT_DBusMessage&)), - this, TQ_SLOT(handleDBusSignal(const TQT_DBusMessage&))); + this, TQ_SLOT(handleDBusSignal(const TQT_DBusMessage&))); // watcher for NameOwnerChanged signals dBusWatch = new TQT_DBusProxy(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, dBusConn); TQObject::connect(dBusWatch, TQ_SIGNAL(dbusSignal(const TQT_DBusMessage&)), - this, TQ_SLOT(handleDBusSignal(const TQT_DBusMessage&))); + this, TQ_SLOT(handleDBusSignal(const TQT_DBusMessage&))); // find already running SystemD TQT_DBusProxy checkSystemD(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, dBusConn); - if( checkSystemD.canSend() ) { + if (checkSystemD.canSend()) + { TQValueList params; params << TQT_DBusData::fromString(SYSTEMD_LOGIN1_SERVICE); TQT_DBusMessage reply = checkSystemD.sendWithReply("NameHasOwner", params); - if (reply.type() == TQT_DBusMessage::ReplyMessage && reply.count() == 1 && reply[0].toBool() ) { + if (reply.type() == TQT_DBusMessage::ReplyMessage && reply.count() == 1 && reply[0].toBool()) + { onDBusServiceRegistered(SYSTEMD_LOGIN1_SERVICE); } } return true; } -/*! - * This function handles D-Bus service registering - */ -void SaverEngine::onDBusServiceRegistered(const TQString& service) { - if( service == SYSTEMD_LOGIN1_SERVICE ) { +// This function handles D-Bus service registering +void SaverEngine::onDBusServiceRegistered(const TQString& service) +{ + if (service == SYSTEMD_LOGIN1_SERVICE) + { // get current systemd session TQT_DBusProxy managerIface(SYSTEMD_LOGIN1_SERVICE, SYSTEMD_LOGIN1_PATH, SYSTEMD_LOGIN1_MANAGER_IFACE, dBusConn); TQT_DBusObjectPath systemdSessionPath = TQT_DBusObjectPath(); - if( managerIface.canSend() ) { + if (managerIface.canSend()) + { TQValueList params; - params << TQT_DBusData::fromUInt32( getpid() ); + params << TQT_DBusData::fromUInt32(getpid()); TQT_DBusMessage reply = managerIface.sendWithReply("GetSessionByPID", params); - if (reply.type() == TQT_DBusMessage::ReplyMessage && reply.count() == 1 ) { + if (reply.type() == TQT_DBusMessage::ReplyMessage && reply.count() == 1) + { systemdSessionPath = reply[0].toObjectPath(); } } // wather for systemd session signals - if( systemdSessionPath.isValid() ) { + if (systemdSessionPath.isValid()) + { systemdSession = new TQT_DBusProxy(SYSTEMD_LOGIN1_SERVICE, systemdSessionPath, SYSTEMD_LOGIN1_SESSION_IFACE, dBusConn); TQObject::connect(systemdSession, TQ_SIGNAL(dbusSignal(const TQT_DBusMessage&)), this, TQ_SLOT(handleDBusSignal(const TQT_DBusMessage&))); @@ -902,41 +661,43 @@ void SaverEngine::onDBusServiceRegistered(const TQString& service) { } } -/*! - * This function handles D-Bus service unregistering - */ -void SaverEngine::onDBusServiceUnregistered(const TQString& service) { - if( service == SYSTEMD_LOGIN1_SERVICE ) { - if( systemdSession ) { +// This function handles D-Bus service unregistering +void SaverEngine::onDBusServiceUnregistered(const TQString& service) +{ + if (service == SYSTEMD_LOGIN1_SERVICE) + { + if (systemdSession) + { delete systemdSession; - systemdSession = 0; + systemdSession = nullptr; } return; } } -/*! - * This function handles signals from the D-Bus daemon. - */ -void SaverEngine::handleDBusSignal(const TQT_DBusMessage& msg) { +// This function handles signals from the D-Bus daemon. +void SaverEngine::handleDBusSignal(const TQT_DBusMessage& msg) +{ // dbus terminated - if( msg.path() == DBUS_PATH_LOCAL - && msg.interface() == DBUS_INTERFACE_LOCAL - && msg.member() == "Disconnected" ) { + if (msg.path() == DBUS_PATH_LOCAL && msg.interface() == DBUS_INTERFACE_LOCAL && + msg.member() == "Disconnected") + { dBusClose(); TQTimer::singleShot(1000, this, TQ_SLOT(dBusReconnect())); return; } // service registered / unregistered - if( msg.path() == DBUS_PATH_DBUS - && msg.interface() == DBUS_INTERFACE_DBUS - && msg.member() == "NameOwnerChanged" ) { - if( msg[1].toString().isEmpty() ) { + if (msg.path() == DBUS_PATH_DBUS && msg.interface() == DBUS_INTERFACE_DBUS && + msg.member() == "NameOwnerChanged") + { + if (msg[1].toString().isEmpty()) + { // old-owner is empty onDBusServiceRegistered(msg[0].toString()); } - if( msg[2].toString().isEmpty() ) { + if (msg[2].toString().isEmpty()) + { // new-owner is empty onDBusServiceUnregistered(msg[0].toString()); } @@ -944,69 +705,364 @@ void SaverEngine::handleDBusSignal(const TQT_DBusMessage& msg) { } // systemd signal Lock() - if( systemdSession && systemdSession->canSend() - && msg.path() == systemdSession->path() - && msg.interface() == SYSTEMD_LOGIN1_SESSION_IFACE - && msg.member() == "Lock") { + if (systemdSession && systemdSession->canSend() && msg.path() == systemdSession->path() && + msg.interface() == SYSTEMD_LOGIN1_SESSION_IFACE && msg.member() == "Lock") + { lockScreen(); return; } // systemd signal Unlock() - if( systemdSession && systemdSession->canSend() - && msg.path() == systemdSession->path() - && msg.interface() == SYSTEMD_LOGIN1_SESSION_IFACE - && msg.member() == "Unlock") { + if (systemdSession && systemdSession->canSend() && msg.path() == systemdSession->path() && + msg.interface() == SYSTEMD_LOGIN1_SESSION_IFACE && msg.member() == "Unlock") + { // unlock? return; } } -bool SaverEngine::waitForLockProcessStart() { - sigset_t new_mask; - sigset_t empty_mask; - sigemptyset(&empty_mask); +void SaverEngine::lockScreenAndDoNewSession() +{ + mNewVTAfterLockEngage = true; + lockScreen(); +} - // ensure that SIGCHLD is not subject to a race condition - sigemptyset(&new_mask); - sigaddset(&new_mask, SIGCHLD); +void SaverEngine::lockScreenAndSwitchSession(int vt) +{ + mSwitchVTAfterLockEngage = vt; + lockScreen(); +} - pthread_sigmask(SIG_BLOCK, &new_mask, NULL); - while ((mLockProcess.isRunning()) && (!mSaverProcessReady)) { - // wait for any signal(s) to arrive - sigsuspend(&empty_mask); +SaverEngineEventHandler::SaverEngineEventHandler(SaverEngine *engine) : + m_state(Waiting), m_saverProcessReady(false), m_lockProcessRestarting(false), + m_terminationRequest(false), m_saverEngine(engine), m_SAKProcess(nullptr) +{ + connect(&m_lockProcess, TQ_SIGNAL(processExited(TDEProcess*)), + this, TQ_SLOT(slotLockProcessExited())); +} + +void SaverEngineEventHandler::terminateLockProcess() +{ + if (m_state == Waiting) + { + kill(m_lockProcess.pid(), SIGKILL); + } + m_lockProcess.detach(); // don't kill it if we crash +} + +void SaverEngineEventHandler::lockProcessExited() +{ + kdDebug(1204) << "SaverEngineEventHandler: lock exited" << endl; + + if (trinity_lockeng_sak_available) + { + startSAKProcess(); + } + if (m_state == Waiting) + { + return; } - pthread_sigmask(SIG_UNBLOCK, &new_mask, NULL); - return mLockProcess.isRunning(); + m_state = Waiting; + TQTimer::singleShot(0, m_saverEngine, TQ_SLOT(lockProcessWaitingGUI())); } -bool SaverEngine::waitForLockEngage() { - sigset_t empty_mask; - sigemptyset(&empty_mask); +void SaverEngineEventHandler::lockProcessFullyActivated() +{ + m_state = Saving; + TQTimer::singleShot(0, m_saverEngine, TQ_SLOT(lockProcessFullyActivatedGUI())); +} - // wait for SIGUSR1, SIGUSR2, SIGTTIN - while ((mLockProcess.isRunning()) && (mState != Waiting) && (mState != Saving)) { - // wait for any signal(s) to arrive - sigsuspend(&empty_mask); +void SaverEngineEventHandler::lockProcessReady() +{ + m_saverProcessReady = true; +} + +void SaverEngineEventHandler::lockScreen(bool dcop) +{ + if (m_lockProcessRestarting) + { + kdDebug(1204) << "SaverEngineEventHandler: lock process is restarting, can't handle lock request" << endl; + return; } - return mLockProcess.isRunning(); + bool ok = true; + if (m_state == Waiting) + { + ok = activateSaverOrLock(ForceLock); + // It takes a while for kdesktop_lock to start and lock the screen. + // Therefore delay the DCOP call until it tells kdesktop that the locking is in effect. + // This is done only for --forcelock . + if (ok && m_state != Saving) + { + if (dcop) + { + TQTimer::singleShot(0, m_saverEngine, TQ_SLOT(lockScreenGUI())); + } + } + } } -void SaverEngine::lockScreenAndDoNewSession() { - mNewVTAfterLockEngage = true; - lockScreen(); +void SaverEngineEventHandler::saveScreen() +{ + if (m_lockProcessRestarting) + { + kdDebug(1204) << "SaverEngineEventHandler: lock process is restarting, can't handle save request" << endl; + return; + } + + if (m_state == Waiting) + { + activateSaverOrLock(DefaultLock); + } } -void SaverEngine::lockScreenAndSwitchSession(int vt) { - mSwitchVTAfterLockEngage = vt; - lockScreen(); +void SaverEngineEventHandler::slotLockProcessExited() +{ + m_lockProcessRestarting = true; + + bool abnormalExit = false; + if (!m_lockProcess.normalExit()) + { + abnormalExit = true; + } + else if (m_lockProcess.exitStatus() != 0) + { + abnormalExit = true; + } + if (m_terminationRequest) + { + abnormalExit = false; + m_terminationRequest = false; + } + + // Restart the lock process. This call blocks till + // the lock process has restarted. + restartLockProcess(); + + if (abnormalExit) + { + // Possible hacking attempt detected, try to relaunch the saver with force lock + m_state = Waiting; + if (!activateSaverOrLock(ForceLock)) + { + TQTimer::singleShot(0, m_saverEngine, TQ_SLOT(terminateTDESession())); + } + } + m_lockProcessRestarting = false; } -void SaverEngineThreadHelperObject::terminateThread() { - TQEventLoop* eventLoop = TQApplication::eventLoop(); - if (eventLoop) { +/* + * Start or restart the lock process. + * On the very first invocation, launch the SAK process if required and + * auto lock the screen if the option has been enabled in the configuration. + */ +bool SaverEngineEventHandler::restartLockProcess() +{ + static bool firstStart = true; + + bool autoLoginEnable = false; + bool autoLoginLocked = false; + if (firstStart) + { + firstStart = false; + + // Create SAK process only if SAK is enabled + struct stat st; + KSimpleConfig *config; + if (stat(KDE_CONFDIR "/tdm/tdmdistrc" , &st) == 0) + { + config = new KSimpleConfig(TQString::fromLatin1(KDE_CONFDIR "/tdm/tdmdistrc")); + } + else + { + config = new KSimpleConfig(TQString::fromLatin1(KDE_CONFDIR "/tdm/tdmrc")); + } + config->setGroup("X-:*-Greeter"); + bool useSAKProcess = false; +#ifdef BUILD_TSAK + useSAKProcess = config->readBoolEntry("UseSAK", false) && KDesktopSettings::useTDESAK(); +#endif + if (useSAKProcess) + { + startSAKProcess(); + } + + // autolock the desktop if required + config->setGroup("X-:0-Core"); + autoLoginEnable = config->readBoolEntry("AutoLoginEnable", false); + autoLoginLocked = config->readBoolEntry("AutoLoginLocked", false); + delete config; + } + + // (Re)start the lock process + if (!m_lockProcess.isRunning()) + { + m_lockProcess.clearArguments(); + TQString path = TDEStandardDirs::findExe("kdesktop_lock"); + if (path.isEmpty()) + { + kdDebug(1204) << "Can't find kdesktop_lock!" << endl; + return false; + } + m_lockProcess << path; + m_lockProcess << TQString("--internal") << TQString("%1").arg(getpid()); + + m_saverProcessReady = false; + if (!m_lockProcess.start()) + { + kdDebug(1204) << "Failed to start kdesktop_lock!" << endl; + return false; + } + // Wait for the lock process to signal that it is ready + sigset_t empty_mask; + sigemptyset(&empty_mask); + while (!m_saverProcessReady) + { + sigsuspend(&empty_mask); + } + if (!m_lockProcess.isRunning()) + { + kdDebug(1204) << "Failed to initialize kdesktop_lock (unexpected termination)!" << endl; + return false; + } + } + + if (autoLoginEnable && autoLoginLocked) + { + m_lockProcess.kill(SIGTTOU); + m_lockProcess.kill(SIGUSR1); + } + return true; +} + +// Start the screen saver or lock screen +bool SaverEngineEventHandler::activateSaverOrLock(LockType lock_type) +{ + if (m_state == Saving) + { + return true; + } + + kdDebug(1204) << "SaverEngineEventHandler: starting saver" << endl; + m_state = Preparing; + if (m_SAKProcess) + { + m_SAKProcess->kill(SIGTERM); + } + + m_saverEngine->enableExports(); + if (!restartLockProcess()) + { + m_state = Waiting; + return false; + } + + switch (lock_type) + { + case ForceLock: + m_lockProcess.kill(SIGUSR1); // Request forcelock + break; + case DontLock: + m_lockProcess.kill(SIGUSR2); // Request dontlock + break; + case SecureDialog: + m_lockProcess.kill(SIGWINCH); // Request secure dialog + break; + default: + break; + } + + if (m_saverEngine->mBlankOnly) + { + m_lockProcess.kill(SIGTTIN); // Request blanking + } + + int ret = m_lockProcess.kill(SIGTTOU); // Start lock + if (!ret) + { + m_state = Waiting; + return false; + } + m_state = Engaging; + + // Ask to the GUI thread to activate X11 saver + TQTimer::singleShot(0, m_saverEngine, TQ_SLOT(activateSaverOrLockGUI())); + + return true; +} + +// Stop the screen saver. +void SaverEngineEventHandler::stopLockProcess() +{ + if (m_state == Waiting) + { + return; + } + + kdDebug(1204) << "SaverEngineEventHandler: stopping lock process" << endl; + + m_terminationRequest = true; + m_lockProcess.kill(); + m_state = Waiting; + + // Ask to the GUI thread to stop the X11 saver + TQTimer::singleShot(0, m_saverEngine, TQ_SLOT(stopLockProcessGUI())); +} + +void SaverEngineEventHandler::startSAKProcess() +{ + if (!m_SAKProcess) + { + m_SAKProcess = new TDEProcess; + *m_SAKProcess << "tdmtsak"; + connect(m_SAKProcess, TQ_SIGNAL(processExited(TDEProcess*)), this, TQ_SLOT(slotSAKProcessExited())); + } + if (!m_SAKProcess->isRunning()) + { + m_SAKProcess->start(); + } +} + +void SaverEngineEventHandler::slotSAKProcessExited() +{ + if (!m_SAKProcess) + { + tqWarning("[kdesktop] SAK process does not exist. Something went wrong. Ignoring."); + return; + } + + int retcode = m_SAKProcess->exitStatus(); + if (retcode && m_SAKProcess->normalExit()) + { + trinity_lockeng_sak_available = false; + tqWarning("[kdesktop] SAK driven secure dialog is not available for use (retcode %d). " + "Check tdmtsak for proper functionality.", retcode); + } + + if (m_state == Preparing) + { + return; + } + + if (m_SAKProcess->normalExit() && trinity_lockeng_sak_available) + { + if (m_state == Waiting) + { + activateSaverOrLock(SecureDialog); + } + else + { + m_lockProcess.kill(SIGHUP); + } + } +} + +void SaverEngineEventHandler::terminateThread() +{ + TQEventLoop *eventLoop = TQApplication::eventLoop(); + if (eventLoop) + { eventLoop->exit(0); } } diff --git a/kdesktop/lockeng.h b/kdesktop/lockeng.h index d9285abc1..94d8f9e44 100644 --- a/kdesktop/lockeng.h +++ b/kdesktop/lockeng.h @@ -1,6 +1,5 @@ -//=========================================================================== // -// This file is part of the KDE project +// This file is part of the TDE project // // Copyright (c) 1999 Martin R. Jones // @@ -8,7 +7,6 @@ #ifndef __LOCKENG_H__ #define __LOCKENG_H__ -#include #include #include #include @@ -23,31 +21,48 @@ class TDECryptographicCardDevice; #else #define TDECryptographicCardDevice void #endif + +/** + * Screen saver engine. Handles communication with the lock process. + * The engine is split into two parts, the 'SaverEngine' running in the GUI thread and + * the 'SaverEngineEventHandler' running in a separate thread and eventloop. + * The 'SaverEngine' handles communication with X11, DCOP and DBUS while the + * 'SaverEngineEventHandler' handles communication with the actual lock process. + * Several actions require cooperation of the two parts, so in various methods + * there will be inter-thread calls (using timers or by emitting signals) to + * trigger the other side remaining logic. + * This complex design is necessary to avoid blocking the main GUI application event loop, + * which has several tasks to manage and therefore can't affort to wait in a suspended state. + * This was previously leading to deadlock when DCOP calls where executed on the secondary + * thread/eventloop, for example when changing desktop while the lock process was restarting. + */ + class DCOPClientTransaction; class TQT_DBusMessage; class TQT_DBusProxy; +class SaverEngineEventHandler; -class SaverEngineThreadHelperObject : public TQObject +// Type of lock screen +enum LockType : int { - TQ_OBJECT - -public slots: - void terminateThread(); - void slotLockProcessWaiting(); - void slotLockProcessFullyActivated(); + DontLock = 0, + DefaultLock, + ForceLock, + SecureDialog +}; -signals: - void lockProcessWaiting(); - void lockProcessFullyActivated(); +enum SaverState +{ + Waiting, + Preparing, + Engaging, + Saving }; -//=========================================================================== -/** - * Screen saver engine. Handles screensaver window, starting screensaver - * hacks, and password entry. - */ -class SaverEngine : public TQWidget, public KScreensaverIface +class SaverEngine : public TQObject, public KScreensaverIface { + friend class SaverEngineEventHandler; + TQ_OBJECT public: SaverEngine(); @@ -100,80 +115,53 @@ public: */ virtual void saverLockReady(); - /** - * @internal - */ void lockScreen(bool DCOP = false); - /** - * Called by KDesktop to wait for saver engage - * @internal - */ - bool waitForLockEngage(); - - /** - * @internal - */ void lockScreenAndDoNewSession(); - - /** - * @internal - */ void lockScreenAndSwitchSession(int vt); + void enableExports(); // Enable wallpaper exports + signals: - void terminateHelperThread(); - void asyncLock(); + void activateSaverOrLockSignal(LockType lock_type); + void lockScreenSignal(bool); + void terminateEventHandlerThread(); public slots: - void slotLockProcessReady(); - void lockProcessWaiting(); - void lockProcessFullyActivated(); void handleDBusSignal(const TQT_DBusMessage&); + void terminateTDESession(); protected slots: void idleTimeout(); - void lockProcessExited(); private slots: - void handleSecureDialog(); - void slotSAKProcessExited(); - void cryptographicCardInserted(TDECryptographicCardDevice*); void cryptographicCardRemoved(TDECryptographicCardDevice*); - - /** - * Enable wallpaper exports - */ - void enableExports(); - void recoverFromHackingAttempt(); void cardStartupTimeout(); - bool dBusReconnect(); + // The following slots are invoked by corresponding methods named without the 'GUI' suffix + // in 'SaverEngineEventHandler' to complete the remaining X11 part of the actions + void activateSaverOrLockGUI(); + void lockProcessFullyActivatedGUI(); + void lockProcessWaitingGUI(); + void lockScreenGUI(); + void stopLockProcessGUI(); + private: - bool restartDesktopLockProcess(); void dBusClose(); bool dBusConnect(); void onDBusServiceRegistered(const TQString&); void onDBusServiceUnregistered(const TQString&); protected: - enum SaverState { Waiting, Preparing, Engaging, Saving }; - enum LockType { DontLock, DefaultLock, ForceLock, SecureDialog }; - bool startLockProcess( LockType lock_type ); - bool waitForLockProcessStart(); - void stopLockProcess(); - bool handleKeyPress(XKeyEvent *xke); void processLockTransactions(); xautolock_corner_t applyManualSettings(int); protected: bool mEnabled; - SaverState mState; XAutoLock *mXAutoLock; - TDEProcess mLockProcess; int mTimeout; // the original X screensaver parameters @@ -182,26 +170,60 @@ protected: int mXBlanking; int mXExposures; - bool mBlankOnly; // only use the blanker, not the defined saver TQValueVector< DCOPClientTransaction* > mLockTransactions; public: - SaverEngineThreadHelperObject* m_threadHelperObject; + bool mBlankOnly; // only use the blanker, not the defined saver // protected + SaverEngineEventHandler *m_saverEngineEventHandler; private: - TQEventLoopThread* m_helperThread; - sigset_t mThreadBlockSet; - TDEProcess* mSAKProcess; - bool mTerminationRequested; - bool mSaverProcessReady; + TQEventLoopThread* m_eventHandlerThread; bool mNewVTAfterLockEngage; bool mValidCryptoCardInserted; int mSwitchVTAfterLockEngage; struct sigaction mSignalAction; TQT_DBusConnection dBusConn; - TQT_DBusProxy* dBusLocal; - TQT_DBusProxy* dBusWatch; - TQT_DBusProxy* systemdSession; + TQT_DBusProxy *dBusLocal; + TQT_DBusProxy *dBusWatch; + TQT_DBusProxy *systemdSession; +}; + +class SaverEngineEventHandler : public TQObject +{ + TQ_OBJECT + +public: + SaverEngineEventHandler(SaverEngine *engine); + + SaverState getState() const { return m_state; } + + void lockProcessExited(); + void lockProcessFullyActivated(); + void lockProcessReady(); + void terminateLockProcess(); + +public slots: + bool activateSaverOrLock(LockType lock_type); + void lockScreen(bool DCOP = false); + bool restartLockProcess(); + void saveScreen(); + void stopLockProcess(); + void terminateThread(); + +protected slots: + void slotLockProcessExited(); + void slotSAKProcessExited(); + +protected: + void startSAKProcess(); + + bool m_saverProcessReady; + bool m_lockProcessRestarting; + bool m_terminationRequest; + SaverState m_state; + SaverEngine *m_saverEngine; + TDEProcess *m_SAKProcess; + TDEProcess m_lockProcess; }; #endif diff --git a/kdesktop/main.cpp b/kdesktop/main.cpp index f3e61e2f2..0465068b6 100644 --- a/kdesktop/main.cpp +++ b/kdesktop/main.cpp @@ -71,7 +71,7 @@ static TDECmdLineOptions options[] = }; bool argb_visual = false; -KDesktopApp *myApp = NULL; +KDesktopApp *myApp = nullptr; // ----------------------------------------------------------------------------- @@ -251,7 +251,7 @@ extern "C" TDE_EXPORT int kdemain( int argc, char **argv ) else XCloseDisplay( dpy ); } - if( myApp == NULL ) + if (!myApp) myApp = new KDesktopApp; #else myApp = new KDesktopApp; @@ -260,9 +260,6 @@ extern "C" TDE_EXPORT int kdemain( int argc, char **argv ) KDesktopSettings::instance(kdesktop_name + "rc"); - bool x_root_hack = args->isSet("x-root"); - bool wait_for_kded = args->isSet("waitforkded"); - // This MUST be created before any widgets are created SaverEngine saver; @@ -279,16 +276,15 @@ extern "C" TDE_EXPORT int kdemain( int argc, char **argv ) myApp->config()->reparseConfiguration(); } - // for the KDE-already-running check in starttde - TDESelectionOwner kde_running( "_KDE_RUNNING", 0 ); - kde_running.claim( false ); + // for the TDE-already-running check in starttde + TDESelectionOwner tde_running( "_KDE_RUNNING", 0 ); + tde_running.claim( false ); + bool x_root_hack = args->isSet("x-root"); + bool wait_for_kded = args->isSet("waitforkded"); KDesktop desktop( &saver, x_root_hack, wait_for_kded ); args->clear(); - myApp->dcopClient()->setDefaultObject( "KDesktopIface" ); - - return myApp->exec(); }