You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebase/kdesktop/lockeng.cpp

1013 lines
27 KiB

//===========================================================================
//
// This file is part of the TDE project
//
// Copyright (c) 1999 Martin R. Jones <mjones@kde.org>
// Copyright (c) 2012 Timothy Pearson <kb9vqf@pearsoncomputing.net>
//
#include <config.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <tdeglobal.h>
#ifdef WITH_TDEHWLIB
#include <ksslcertificate.h>
#include <kuser.h>
#include <tdehardwaredevices.h>
#include <tdecryptographiccarddevice.h>
#endif
#include <kstandarddirs.h>
#include <tdeapplication.h>
#include <kservicegroup.h>
#include <ksimpleconfig.h>
#include <kdebug.h>
#include <tdelocale.h>
#include <tqfile.h>
#include <tqtimer.h>
#include <tqeventloop.h>
#include <dcopclient.h>
#include <assert.h>
#include <dmctl.h>
#include <dbus/dbus-shared.h>
#include <tqdbusdata.h>
#include <tqdbuserror.h>
#include <tqdbusmessage.h>
#include <tqdbusobjectpath.h>
#include <tqdbusproxy.h>
#include "lockeng.h"
#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"
#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;
static void sigusr1_handler(int)
{
if (m_masterSaverEngine) {
m_masterSaverEngine->m_threadHelperObject->slotLockProcessWaiting();
}
}
static void sigusr2_handler(int)
{
if (m_masterSaverEngine) {
m_masterSaverEngine->m_threadHelperObject->slotLockProcessFullyActivated();
}
}
static void sigttin_handler(int)
{
if (m_masterSaverEngine) {
m_masterSaverEngine->slotLockProcessReady();
}
}
//===========================================================================
//
// 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(),
KScreensaverIface(),
mBlankOnly(false),
mSAKProcess(NULL),
mTerminationRequested(false),
mSaverProcessReady(false),
mNewVTAfterLockEngage(false),
mValidCryptoCardInserted(false),
mSwitchVTAfterLockEngage(-1),
dBusLocal(0),
dBusWatch(0),
systemdSession(0)
{
// handle SIGUSR1
m_masterSaverEngine = this;
mSignalAction.sa_handler= sigusr1_handler;
sigemptyset(&(mSignalAction.sa_mask));
sigaddset(&(mSignalAction.sa_mask), SIGUSR1);
mSignalAction.sa_flags = 0;
sigaction(SIGUSR1, &mSignalAction, 0L);
// handle SIGUSR2
m_masterSaverEngine = this;
mSignalAction.sa_handler= sigusr2_handler;
sigemptyset(&(mSignalAction.sa_mask));
sigaddset(&(mSignalAction.sa_mask), SIGUSR2);
mSignalAction.sa_flags = 0;
sigaction(SIGUSR2, &mSignalAction, 0L);
// handle SIGTTIN
m_masterSaverEngine = this;
mSignalAction.sa_handler= sigttin_handler;
sigemptyset(&(mSignalAction.sa_mask));
sigaddset(&(mSignalAction.sa_mask), SIGTTIN);
mSignalAction.sa_flags = 0;
sigaction(SIGTTIN, &mSignalAction, 0L);
// Save X screensaver parameters
XGetScreenSaver(tqt_xdisplay(), &mXTimeout, &mXInterval,
&mXBlanking, &mXExposures);
mState = Waiting;
mXAutoLock = 0;
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() == false )
{
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;
#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<TDECryptographicCardDevice*>(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);
}
// Check card login status
KUser userinfo;
TQString fileName = userinfo.homeDir() + "/.tde_card_login_state";
TQFile flagFile(fileName);
if (flagFile.open(IO_ReadOnly)) {
TQTextStream stream(&flagFile);
if (stream.readLine().startsWith("1")) {
// Card was likely used to log in
TQTimer::singleShot(5000, this, TQ_SLOT(cardStartupTimeout()));
}
flagFile.close();
}
#endif
dBusConnect();
}
//---------------------------------------------------------------------------
//
// Destructor - usual cleanups.
//
SaverEngine::~SaverEngine()
{
if (mState == Waiting) {
kill(mLockProcess.pid(), SIGKILL);
}
mLockProcess.detach(); // don't kill it if we crash
delete mXAutoLock;
dBusClose();
// Restore X screensaver parameters
XSetScreenSaver(tqt_xdisplay(), mXTimeout, mXInterval, mXBlanking,
mXExposures);
terminateHelperThread();
m_helperThread->wait();
delete m_threadHelperObject;
delete m_helperThread;
}
void SaverEngine::cardStartupTimeout() {
if (!mValidCryptoCardInserted) {
// Restore saver timeout
configure();
// Force lock
lockScreen();
}
}
void SaverEngine::cryptographicCardInserted(TDECryptographicCardDevice* cdevice) {
#ifdef WITH_TDEHWLIB
TQString login_name = TQString::null;
X509CertificatePtrList certList = cdevice->cardX509Certificates();
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 ) {
TQString lcpart = (*it).lower();
if (lcpart.startsWith("cn=")) {
login_name = lcpart.right(lcpart.length() - strlen("cn="));
}
}
delete card_cert;
}
if (login_name != "") {
KUser user;
if (login_name == user.loginName()) {
mValidCryptoCardInserted = true;
}
}
#endif
}
void SaverEngine::cryptographicCardRemoved(TDECryptographicCardDevice* cdevice) {
#ifdef WITH_TDEHWLIB
if (mValidCryptoCardInserted) {
mValidCryptoCardInserted = false;
// Restore saver timeout
configure();
// Force lock
lockScreen();
}
#endif
}
//---------------------------------------------------------------------------
//
// This should be called only using DCOP.
//
void SaverEngine::lock()
{
lockScreen(true);
}
//---------------------------------------------------------------------------
//
// Lock the screen
//
void SaverEngine::lockScreen(bool DCOP)
{
if (mValidCryptoCardInserted) {
return;
}
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 = kapp->dcopClient()->beginTransaction();
if (trans) {
mLockTransactions.append( trans );
}
}
}
}
else
{
mLockProcess.kill( SIGHUP );
}
}
void SaverEngine::processLockTransactions()
{
for( TQValueVector< DCOPClientTransaction* >::ConstIterator it = mLockTransactions.begin();
it != mLockTransactions.end();
++it )
{
TQCString replyType = "void";
TQByteArray arr;
kapp->dcopClient()->endTransaction( *it, replyType, arr );
}
mLockTransactions.clear();
}
void SaverEngine::saverLockReady()
{
if( mState != Engaging )
{
kdDebug( 1204 ) << "Got unexpected saverReady()" << endl;
}
kdDebug( 1204 ) << "Saver Lock Ready" << endl;
processLockTransactions();
}
//---------------------------------------------------------------------------
void SaverEngine::save()
{
if (!mValidCryptoCardInserted) {
if (mState == Waiting) {
startLockProcess( DefaultLock );
}
}
}
//---------------------------------------------------------------------------
void SaverEngine::quit()
{
if (mState == Saving || mState == Engaging)
{
stopLockProcess();
}
}
//---------------------------------------------------------------------------
bool SaverEngine::isEnabled()
{
return mEnabled;
}
//---------------------------------------------------------------------------
bool SaverEngine::enable( bool e )
{
if ( e == mEnabled )
return true;
// If we aren't in a suitable state, we will not reconfigure.
if (mState != Waiting)
return false;
mEnabled = e;
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;
}
else {
if (mXAutoLock) {
delete mXAutoLock;
mXAutoLock = 0;
}
XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset );
XSetScreenSaver(tqt_xdisplay(), 0, mXInterval, PreferBlanking, DontAllowExposures);
kdDebug(1204) << "Saver Engine disabled" << endl;
}
return true;
}
//---------------------------------------------------------------------------
bool SaverEngine::isBlanked()
{
return (mState != Waiting);
}
void SaverEngine::enableExports()
{
#ifdef TQ_WS_X11
kdDebug(270) << k_lineinfo << "activating background exports.\n";
DCOPClient *client = kapp->dcopClient();
if (!client->isAttached()) {
client->attach();
}
TQByteArray data;
TQDataStream args( data, IO_WriteOnly );
args << 1;
TQCString appname( "kdesktop" );
int screen_number = DefaultScreen(tqt_xdisplay());
if ( screen_number ) {
appname.sprintf("kdesktop-screen-%d", screen_number );
}
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 == TRUE)) {
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) {
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()
int action;
action = KDesktopSettings::actionTopLeft();
xautolock_corners[0] = applyManualSettings(action);
action = KDesktopSettings::actionTopRight();
xautolock_corners[1] = applyManualSettings(action);
action = KDesktopSettings::actionBottomLeft();
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 )
{
mBlankOnly = blankOnly;
// FIXME: if running, stop and restart? What about security
// implications of this?
}
bool SaverEngine::restartDesktopLockProcess()
{
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() == false) {
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) {
mXAutoLock->stop();
}
return true;
}
//---------------------------------------------------------------------------
//
// Stop the screen saver.
//
void SaverEngine::stopLockProcess()
{
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) {
mXAutoLock->start();
}
XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset );
XSetScreenSaver(tqt_xdisplay(), mTimeout + 10, mXInterval, PreferBlanking, mXExposures);
}
processLockTransactions();
mState = Waiting;
if( systemdSession && systemdSession->canSend() ) {
TQValueList<TQT_DBusData> params;
params << TQT_DBusData::fromBool(false);
TQT_DBusMessage reply = systemdSession->sendWithReply("SetIdleHint", params);
}
}
void SaverEngine::recoverFromHackingAttempt()
{
// 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 (!kapp->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() == false) {
abnormalExit = true;
}
else {
if (mLockProcess.exitStatus() != 0) {
abnormalExit = true;
}
}
if (mTerminationRequested == true) {
abnormalExit = false;
mTerminationRequested = false;
}
if (abnormalExit == true) {
// PROBABLE HACKING ATTEMPT DETECTED
restartDesktopLockProcess();
mState = Waiting;
TQTimer::singleShot( 100, this, TQ_SLOT(recoverFromHackingAttempt()) );
}
else {
// Restart the lock process
restartDesktopLockProcess();
}
}
void SaverEngineThreadHelperObject::slotLockProcessWaiting()
{
// 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() ) {
TQValueList<TQT_DBusData> params;
params << TQT_DBusData::fromBool(true);
TQT_DBusMessage reply = systemdSession->sendWithReply("SetIdleHint", params);
}
if (mNewVTAfterLockEngage) {
DM().startReserve();
mNewVTAfterLockEngage = false;
}
else if (mSwitchVTAfterLockEngage != -1) {
DM().switchVT(mSwitchVTAfterLockEngage);
mSwitchVTAfterLockEngage = -1;
}
}
void SaverEngine::slotLockProcessReady()
{
mSaverProcessReady = true;
}
void SaverEngine::lockProcessWaiting()
{
kdDebug(1204) << "SaverEngine: lock exited" << endl;
if (trinity_lockeng_sak_available == TRUE) {
handleSecureDialog();
}
if( mState == Waiting ) {
return;
}
emitDCOPSignal("KDE_stop_screensaver()", TQByteArray());
if (mEnabled) {
if (mXAutoLock) {
mXAutoLock->start();
}
XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset );
XSetScreenSaver(tqt_xdisplay(), mTimeout + 10, mXInterval, PreferBlanking, mXExposures);
}
processLockTransactions();
mState = Waiting;
if( systemdSession && systemdSession->canSend() ) {
TQValueList<TQT_DBusData> 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) {
// disable X screensaver
XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset );
XSetScreenSaver(tqt_xdisplay(), 0, mXInterval, PreferBlanking, DontAllowExposures);
startLockProcess( DefaultLock );
}
}
xautolock_corner_t SaverEngine::applyManualSettings(int action)
{
if (action == 0) {
kdDebug() << "no lock" << endl;
return ca_nothing;
}
else if (action == 1) {
kdDebug() << "lock screen" << endl;
return ca_forceLock;
}
else if (action == 2) {
kdDebug() << "prevent lock" << endl;
return ca_dontLock;
}
else{
kdDebug() << "no lock nothing" << endl;
return ca_nothing;
}
}
/*!
* This function try a 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());
}
/*!
* This function is used to close D-Bus connection.
*/
void SaverEngine::dBusClose() {
if( dBusConn.isConnected() ) {
if( dBusLocal ) {
delete dBusLocal;
dBusLocal = 0;
}
if( dBusWatch ) {
delete dBusWatch;
dBusWatch = 0;
}
if( systemdSession ) {
delete systemdSession;
systemdSession = 0;
}
}
dBusConn.closeConnection(DBUS_CONN_NAME);
}
/*!
* 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() ) {
kdError() << "Failed to open connection to system message bus: " << dBusConn.lastError().message() << endl;
TQTimer::singleShot(4000, this, TQ_SLOT(dBusReconnect()));
return false;
}
// 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&)));
// 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&)));
// find already running SystemD
TQT_DBusProxy checkSystemD(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, dBusConn);
if( checkSystemD.canSend() ) {
TQValueList<TQT_DBusData> 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() ) {
onDBusServiceRegistered(SYSTEMD_LOGIN1_SERVICE);
}
}
return true;
}
/*!
* 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() ) {
TQValueList<TQT_DBusData> params;
params << TQT_DBusData::fromUInt32( getpid() );
TQT_DBusMessage reply = managerIface.sendWithReply("GetSessionByPID", params);
if (reply.type() == TQT_DBusMessage::ReplyMessage && reply.count() == 1 ) {
systemdSessionPath = reply[0].toObjectPath();
}
}
// wather for systemd session signals
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&)));
}
return;
}
}
/*!
* This function handles D-Bus service unregistering
*/
void SaverEngine::onDBusServiceUnregistered(const TQString& service) {
if( service == SYSTEMD_LOGIN1_SERVICE ) {
if( systemdSession ) {
delete systemdSession;
systemdSession = 0;
}
return;
}
}
/*!
* 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" ) {
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() ) {
// old-owner is empty
onDBusServiceRegistered(msg[0].toString());
}
if( msg[2].toString().isEmpty() ) {
// new-owner is empty
onDBusServiceUnregistered(msg[0].toString());
}
return;
}
// systemd signal 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") {
// unlock?
return;
}
}
bool SaverEngine::waitForLockProcessStart() {
sigset_t new_mask;
sigset_t empty_mask;
sigemptyset(&empty_mask);
// ensure that SIGCHLD is not subject to a race condition
sigemptyset(&new_mask);
sigaddset(&new_mask, SIGCHLD);
pthread_sigmask(SIG_BLOCK, &new_mask, NULL);
while ((mLockProcess.isRunning()) && (!mSaverProcessReady)) {
// wait for any signal(s) to arrive
sigsuspend(&empty_mask);
}
pthread_sigmask(SIG_UNBLOCK, &new_mask, NULL);
return mLockProcess.isRunning();
}
bool SaverEngine::waitForLockEngage() {
sigset_t empty_mask;
sigemptyset(&empty_mask);
// wait for SIGUSR1, SIGUSR2, SIGTTIN
while ((mLockProcess.isRunning()) && (mState != Waiting) && (mState != Saving)) {
// wait for any signal(s) to arrive
sigsuspend(&empty_mask);
}
return mLockProcess.isRunning();
}
void SaverEngine::lockScreenAndDoNewSession() {
mNewVTAfterLockEngage = true;
lockScreen();
}
void SaverEngine::lockScreenAndSwitchSession(int vt) {
mSwitchVTAfterLockEngage = vt;
lockScreen();
}
void SaverEngineThreadHelperObject::terminateThread() {
TQEventLoop* eventLoop = TQApplication::eventLoop();
if (eventLoop) {
eventLoop->exit(0);
}
}