You cannot 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

1069 lines
26 KiB
C++

//===========================================================================
//
// 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 <tdestandarddirs.h>
#include <tdeapplication.h>
#include <kservicegroup.h>
#include <tdesimpleconfig.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"
#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"
extern xautolock_corner_t xautolock_corners[ 4 ];
bool trinity_lockeng_sak_available = true;
SaverEngineEventHandler *gbl_saverEngineEventHandler = nullptr;
static void sigusr1_handler(int)
{
if (gbl_saverEngineEventHandler)
{
gbl_saverEngineEventHandler->lockProcessExited();
}
}
static void sigusr2_handler(int)
{
if (gbl_saverEngineEventHandler)
{
gbl_saverEngineEventHandler->lockProcessFullyActivated();
}
}
static void sigttin_handler(int)
{
if (gbl_saverEngineEventHandler)
{
gbl_saverEngineEventHandler->lockProcessReady();
}
}
SaverEngine::SaverEngine()
: TQObject(),
KScreensaverIface(),
mBlankOnly(false),
mNewVTAfterLockEngage(false),
mValidCryptoCardInserted(false),
mSwitchVTAfterLockEngage(-1),
dBusLocal(0),
dBusWatch(0),
systemdSession(0)
{
// handle SIGUSR1
mSignalAction.sa_handler= sigusr1_handler;
sigemptyset(&(mSignalAction.sa_mask));
sigaddset(&(mSignalAction.sa_mask), SIGUSR1);
mSignalAction.sa_flags = 0;
sigaction(SIGUSR1, &mSignalAction, 0L);
// handle SIGUSR2
mSignalAction.sa_handler= sigusr2_handler;
sigemptyset(&(mSignalAction.sa_mask));
sigaddset(&(mSignalAction.sa_mask), SIGUSR2);
mSignalAction.sa_flags = 0;
sigaction(SIGUSR2, &mSignalAction, 0L);
// handle SIGTTIN
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);
// 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;
configure();
// 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<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();
}
SaverEngine::~SaverEngine()
{
m_saverEngineEventHandler->terminateLockProcess();
delete mXAutoLock;
dBusClose();
// Restore X screensaver parameters
XSetScreenSaver(tqt_xdisplay(), mXTimeout, mXInterval, mXBlanking, mXExposures);
emit terminateEventHandlerThread();
m_eventHandlerThread->wait();
delete m_saverEngineEventHandler;
delete m_eventHandlerThread;
}
void SaverEngine::cardStartupTimeout()
{
if (!mValidCryptoCardInserted)
{
configure(); // Restore saver timeout
lockScreen(); // Force lock
}
}
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
}
// DCOP interface method
void SaverEngine::lock()
{
lockScreen(true);
}
void SaverEngine::lockScreen(bool dcop)
{
if (mValidCryptoCardInserted)
{
kdDebug(1204) << "SaverEngine: crypto card inserted, ignore lock request" << endl;
return;
}
emit lockScreenSignal(dcop);
}
void SaverEngine::lockScreenGUI()
{
DCOPClientTransaction *trans = tdeApp->dcopClient()->beginTransaction();
if (trans)
{
mLockTransactions.append(trans);
}
}
void SaverEngine::processLockTransactions()
{
TQValueVector<DCOPClientTransaction*>::ConstIterator it = mLockTransactions.begin();
for (; it != mLockTransactions.end(); ++it)
{
TQCString replyType = "void";
TQByteArray arr;
tdeApp->dcopClient()->endTransaction(*it, replyType, arr);
}
mLockTransactions.clear();
}
void SaverEngine::saverLockReady()
{
if (m_saverEngineEventHandler->getState() != SaverState::Engaging)
{
kdDebug(1204) << "Got unexpected saverLockReady()" << endl;
}
kdDebug(1204) << "Saver Lock Ready" << endl;
processLockTransactions();
}
// DCOP interface method
void SaverEngine::save()
{
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()
{
TQTimer::singleShot(0, m_saverEngineEventHandler, TQ_SLOT(stopLockProcess()));
}
// DCOP interface method
bool SaverEngine::isEnabled()
{
return mEnabled;
}
// DCOP interface method
bool SaverEngine::enable(bool e)
{
if (e == mEnabled)
return true;
// If we aren't in a suitable state, we will not reconfigure.
if (m_saverEngineEventHandler->getState() != SaverState::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);
// We'll handle blanking
XSetScreenSaver(tqt_xdisplay(), mTimeout + 10, mXInterval, PreferBlanking, mXExposures);
mXAutoLock->start();
kdDebug(1204) << "Saver engine started, timeout: " << mTimeout << endl;
}
else
{
if (mXAutoLock)
{
delete mXAutoLock;
mXAutoLock = nullptr;
}
XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset);
XSetScreenSaver(tqt_xdisplay(), 0, mXInterval, PreferBlanking, DontAllowExposures);
kdDebug(1204) << "Saver engine disabled" << endl;
}
return true;
}
// DCOP interface method
bool SaverEngine::isBlanked()
{
return (m_saverEngineEventHandler->getState() != SaverState::Waiting);
}
void SaverEngine::enableExports()
{
#ifdef TQ_WS_X11
kdDebug(270) << k_lineinfo << "activating background exports" << endl;
DCOPClient *client = tdeApp->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
}
// Read and apply configuration.
void SaverEngine::configure()
{
// If we aren't in a suitable state, we will not reconfigure.
if (m_saverEngineEventHandler->getState() != SaverState::Waiting)
{
return;
}
// create a new config obj to ensure we read the latest options
KDesktopSettings::self()->readConfig();
mTimeout = KDesktopSettings::timeout();
bool e = KDesktopSettings::screenSaverEnabled();
mEnabled = !e; // enable the screensaver by forcibly toggling it
enable(e);
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);
}
// 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;
}
void SaverEngine::activateSaverOrLockGUI()
{
XSetScreenSaver(tqt_xdisplay(), 0, mXInterval, PreferBlanking, mXExposures);
if (mXAutoLock)
{
mXAutoLock->stop();
}
emitDCOPSignal("KDE_start_screensaver()", TQByteArray());
}
void SaverEngine::stopLockProcessGUI()
{
emitDCOPSignal("KDE_stop_screensaver()", TQByteArray());
if (mEnabled)
{
if (mXAutoLock)
{
mXAutoLock->start();
}
XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset);
XSetScreenSaver(tqt_xdisplay(), mTimeout + 10, mXInterval, PreferBlanking, mXExposures);
}
processLockTransactions();
if (systemdSession && systemdSession->canSend())
{
TQValueList<TQT_DBusData> params;
params << TQT_DBusData::fromBool(false);
TQT_DBusMessage reply = systemdSession->sendWithReply("SetIdleHint", params);
}
}
void SaverEngine::terminateTDESession()
{
// 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::lockProcessFullyActivatedGUI()
{
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::lockProcessWaitingGUI()
{
emitDCOPSignal("KDE_stop_screensaver()", TQByteArray());
if (mEnabled)
{
if (mXAutoLock)
{
mXAutoLock->start();
}
XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset);
XSetScreenSaver(tqt_xdisplay(), mTimeout + 10, mXInterval, PreferBlanking, mXExposures);
}
processLockTransactions();
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);
emit activateSaverOrLockSignal(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 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()
{
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)
{
delete dBusLocal;
dBusLocal = nullptr;
}
if (dBusWatch)
{
delete dBusWatch;
dBusWatch = nullptr;
}
if (systemdSession)
{
delete systemdSession;
systemdSession = nullptr;
}
}
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 = nullptr;
}
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;
}
}
void SaverEngine::lockScreenAndDoNewSession()
{
mNewVTAfterLockEngage = true;
lockScreen();
}
void SaverEngine::lockScreenAndSwitchSession(int vt)
{
mSwitchVTAfterLockEngage = vt;
lockScreen();
}
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;
}
m_state = Waiting;
TQTimer::singleShot(0, m_saverEngine, TQ_SLOT(lockProcessWaitingGUI()));
}
void SaverEngineEventHandler::lockProcessFullyActivated()
{
m_state = Saving;
TQTimer::singleShot(0, m_saverEngine, TQ_SLOT(lockProcessFullyActivatedGUI()));
}
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;
}
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 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 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;
}
/*
* 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;
TDESimpleConfig *config;
if (stat(KDE_CONFDIR "/tdm/tdmdistrc" , &st) == 0)
{
config = new TDESimpleConfig(TQString::fromLatin1(KDE_CONFDIR "/tdm/tdmdistrc"));
}
else
{
config = new TDESimpleConfig(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);
}
}