summaryrefslogtreecommitdiffstats
path: root/ksmserver/server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ksmserver/server.cpp')
-rw-r--r--ksmserver/server.cpp922
1 files changed, 922 insertions, 0 deletions
diff --git a/ksmserver/server.cpp b/ksmserver/server.cpp
new file mode 100644
index 000000000..2fcb83785
--- /dev/null
+++ b/ksmserver/server.cpp
@@ -0,0 +1,922 @@
+/*****************************************************************
+ksmserver - the KDE session management server
+
+Copyright (C) 2000 Matthias Ettrich <ettrich@kde.org>
+Copyright (C) 2005 Lubos Lunak <l.lunak@kde.org>
+
+relatively small extensions by Oswald Buddenhagen <ob6@inf.tu-dresden.de>
+
+some code taken from the dcopserver (part of the KDE libraries), which is
+Copyright (c) 1999 Matthias Ettrich <ettrich@kde.org>
+Copyright (c) 1999 Preston Brown <pbrown@kde.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qdatastream.h>
+#include <qptrstack.h>
+#include <qpushbutton.h>
+#include <qmessagebox.h>
+#include <qguardedptr.h>
+#include <qtimer.h>
+#include <qregexp.h>
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <kstandarddirs.h>
+#include <unistd.h>
+#include <kapplication.h>
+#include <kstaticdeleter.h>
+#include <ktempfile.h>
+#include <kprocess.h>
+#include <dcopclient.h>
+#include <dcopref.h>
+
+#include "server.h"
+#include "global.h"
+#include "client.h"
+
+#include "server.moc"
+
+#include <kdebug.h>
+
+#include <dmctl.h>
+
+KSMServer* the_server = 0;
+
+KSMServer* KSMServer::self()
+{
+ return the_server;
+}
+
+/*! Utility function to execute a command on the local machine. Used
+ * to restart applications.
+ */
+void KSMServer::startApplication( QStringList command, const QString& clientMachine,
+ const QString& userId )
+{
+ if ( command.isEmpty() )
+ return;
+ if ( !userId.isEmpty()) {
+ struct passwd* pw = getpwuid( getuid());
+ if( pw != NULL && userId != QString::fromLocal8Bit( pw->pw_name )) {
+ command.prepend( "--" );
+ command.prepend( userId );
+ command.prepend( "-u" );
+ command.prepend( "kdesu" );
+ }
+ }
+ if ( !clientMachine.isEmpty() && clientMachine != "localhost" ) {
+ command.prepend( clientMachine );
+ command.prepend( xonCommand ); // "xon" by default
+ }
+ int n = command.count();
+ QCString app = command[0].latin1();
+ QValueList<QCString> argList;
+ for ( int i=1; i < n; i++)
+ argList.append( QCString(command[i].latin1()));
+ DCOPRef( launcher ).send( "exec_blind", app, DCOPArg( argList, "QValueList<QCString>" ) );
+}
+
+/*! Utility function to execute a command on the local machine. Used
+ * to discard session data
+ */
+void KSMServer::executeCommand( const QStringList& command )
+{
+ if ( command.isEmpty() )
+ return;
+ KProcess proc;
+ for ( QStringList::ConstIterator it = command.begin();
+ it != command.end(); ++it )
+ proc << (*it).latin1();
+ proc.start( KProcess::Block );
+}
+
+IceAuthDataEntry *authDataEntries = 0;
+static KTempFile *remAuthFile = 0;
+
+static IceListenObj *listenObjs = 0;
+int numTransports = 0;
+static bool only_local = 0;
+
+static Bool HostBasedAuthProc ( char* /*hostname*/)
+{
+ if (only_local)
+ return true;
+ else
+ return false;
+}
+
+
+Status KSMRegisterClientProc (
+ SmsConn /* smsConn */,
+ SmPointer managerData,
+ char * previousId
+)
+{
+ KSMClient* client = (KSMClient*) managerData;
+ client->registerClient( previousId );
+ return 1;
+}
+
+void KSMInteractRequestProc (
+ SmsConn /* smsConn */,
+ SmPointer managerData,
+ int dialogType
+)
+{
+ the_server->interactRequest( (KSMClient*) managerData, dialogType );
+}
+
+void KSMInteractDoneProc (
+ SmsConn /* smsConn */,
+ SmPointer managerData,
+ Bool cancelShutdown
+)
+{
+ the_server->interactDone( (KSMClient*) managerData, cancelShutdown );
+}
+
+void KSMSaveYourselfRequestProc (
+ SmsConn smsConn ,
+ SmPointer /* managerData */,
+ int saveType,
+ Bool shutdown,
+ int interactStyle,
+ Bool fast,
+ Bool global
+)
+{
+ if ( shutdown ) {
+ the_server->shutdown( fast ?
+ KApplication::ShutdownConfirmNo :
+ KApplication::ShutdownConfirmDefault,
+ KApplication::ShutdownTypeDefault,
+ KApplication::ShutdownModeDefault );
+ } else if ( !global ) {
+ SmsSaveYourself( smsConn, saveType, false, interactStyle, fast );
+ SmsSaveComplete( smsConn );
+ }
+ // else checkpoint only, ksmserver does not yet support this
+ // mode. Will come for KDE 3.1
+}
+
+void KSMSaveYourselfPhase2RequestProc (
+ SmsConn /* smsConn */,
+ SmPointer managerData
+)
+{
+ the_server->phase2Request( (KSMClient*) managerData );
+}
+
+void KSMSaveYourselfDoneProc (
+ SmsConn /* smsConn */,
+ SmPointer managerData,
+ Bool success
+)
+{
+ the_server->saveYourselfDone( (KSMClient*) managerData, success );
+}
+
+void KSMCloseConnectionProc (
+ SmsConn smsConn,
+ SmPointer managerData,
+ int count,
+ char ** reasonMsgs
+)
+{
+ the_server->deleteClient( ( KSMClient* ) managerData );
+ if ( count )
+ SmFreeReasons( count, reasonMsgs );
+ IceConn iceConn = SmsGetIceConnection( smsConn );
+ SmsCleanUp( smsConn );
+ IceSetShutdownNegotiation (iceConn, False);
+ IceCloseConnection( iceConn );
+}
+
+void KSMSetPropertiesProc (
+ SmsConn /* smsConn */,
+ SmPointer managerData,
+ int numProps,
+ SmProp ** props
+)
+{
+ KSMClient* client = ( KSMClient* ) managerData;
+ for ( int i = 0; i < numProps; i++ ) {
+ SmProp *p = client->property( props[i]->name );
+ if ( p ) {
+ client->properties.removeRef( p );
+ SmFreeProperty( p );
+ }
+ client->properties.append( props[i] );
+ if ( !qstrcmp( props[i]->name, SmProgram ) )
+ the_server->clientSetProgram( client );
+ }
+
+ if ( numProps )
+ free( props );
+
+}
+
+void KSMDeletePropertiesProc (
+ SmsConn /* smsConn */,
+ SmPointer managerData,
+ int numProps,
+ char ** propNames
+)
+{
+ KSMClient* client = ( KSMClient* ) managerData;
+ for ( int i = 0; i < numProps; i++ ) {
+ SmProp *p = client->property( propNames[i] );
+ if ( p ) {
+ client->properties.removeRef( p );
+ SmFreeProperty( p );
+ }
+ }
+}
+
+void KSMGetPropertiesProc (
+ SmsConn smsConn,
+ SmPointer managerData
+)
+{
+ KSMClient* client = ( KSMClient* ) managerData;
+ SmProp** props = new SmProp*[client->properties.count()];
+ int i = 0;
+ for ( SmProp* prop = client->properties.first(); prop; prop = client->properties.next() )
+ props[i++] = prop;
+
+ SmsReturnProperties( smsConn, i, props );
+ delete [] props;
+}
+
+
+class KSMListener : public QSocketNotifier
+{
+public:
+ KSMListener( IceListenObj obj )
+ : QSocketNotifier( IceGetListenConnectionNumber( obj ),
+ QSocketNotifier::Read, 0, 0)
+{
+ listenObj = obj;
+}
+
+ IceListenObj listenObj;
+};
+
+class KSMConnection : public QSocketNotifier
+{
+ public:
+ KSMConnection( IceConn conn )
+ : QSocketNotifier( IceConnectionNumber( conn ),
+ QSocketNotifier::Read, 0, 0 )
+ {
+ iceConn = conn;
+ }
+
+ IceConn iceConn;
+};
+
+
+/* for printing hex digits */
+static void fprintfhex (FILE *fp, unsigned int len, char *cp)
+{
+ static const char hexchars[] = "0123456789abcdef";
+
+ for (; len > 0; len--, cp++) {
+ unsigned char s = *cp;
+ putc(hexchars[s >> 4], fp);
+ putc(hexchars[s & 0x0f], fp);
+ }
+}
+
+/*
+ * We use temporary files which contain commands to add/remove entries from
+ * the .ICEauthority file.
+ */
+static void write_iceauth (FILE *addfp, FILE *removefp, IceAuthDataEntry *entry)
+{
+ fprintf (addfp,
+ "add %s \"\" %s %s ",
+ entry->protocol_name,
+ entry->network_id,
+ entry->auth_name);
+ fprintfhex (addfp, entry->auth_data_length, entry->auth_data);
+ fprintf (addfp, "\n");
+
+ fprintf (removefp,
+ "remove protoname=%s protodata=\"\" netid=%s authname=%s\n",
+ entry->protocol_name,
+ entry->network_id,
+ entry->auth_name);
+}
+
+
+#define MAGIC_COOKIE_LEN 16
+
+Status SetAuthentication_local (int count, IceListenObj *listenObjs)
+{
+ int i;
+ for (i = 0; i < count; i ++) {
+ char *prot = IceGetListenConnectionString(listenObjs[i]);
+ if (!prot) continue;
+ char *host = strchr(prot, '/');
+ char *sock = 0;
+ if (host) {
+ *host=0;
+ host++;
+ sock = strchr(host, ':');
+ if (sock) {
+ *sock = 0;
+ sock++;
+ }
+ }
+ kdDebug( 1218 ) << "KSMServer: SetAProc_loc: conn " << (unsigned)i << ", prot=" << prot << ", file=" << sock << endl;
+ if (sock && !strcmp(prot, "local")) {
+ chmod(sock, 0700);
+ }
+ IceSetHostBasedAuthProc (listenObjs[i], HostBasedAuthProc);
+ free(prot);
+ }
+ return 1;
+}
+
+Status SetAuthentication (int count, IceListenObj *listenObjs,
+ IceAuthDataEntry **authDataEntries)
+{
+ KTempFile addAuthFile;
+ addAuthFile.setAutoDelete(true);
+
+ remAuthFile = new KTempFile;
+ remAuthFile->setAutoDelete(true);
+
+ if ((addAuthFile.status() != 0) || (remAuthFile->status() != 0))
+ return 0;
+
+ if ((*authDataEntries = (IceAuthDataEntry *) malloc (
+ count * 2 * sizeof (IceAuthDataEntry))) == NULL)
+ return 0;
+
+ for (int i = 0; i < numTransports * 2; i += 2) {
+ (*authDataEntries)[i].network_id =
+ IceGetListenConnectionString (listenObjs[i/2]);
+ (*authDataEntries)[i].protocol_name = (char *) "ICE";
+ (*authDataEntries)[i].auth_name = (char *) "MIT-MAGIC-COOKIE-1";
+
+ (*authDataEntries)[i].auth_data =
+ IceGenerateMagicCookie (MAGIC_COOKIE_LEN);
+ (*authDataEntries)[i].auth_data_length = MAGIC_COOKIE_LEN;
+
+ (*authDataEntries)[i+1].network_id =
+ IceGetListenConnectionString (listenObjs[i/2]);
+ (*authDataEntries)[i+1].protocol_name = (char *) "XSMP";
+ (*authDataEntries)[i+1].auth_name = (char *) "MIT-MAGIC-COOKIE-1";
+
+ (*authDataEntries)[i+1].auth_data =
+ IceGenerateMagicCookie (MAGIC_COOKIE_LEN);
+ (*authDataEntries)[i+1].auth_data_length = MAGIC_COOKIE_LEN;
+
+ write_iceauth (addAuthFile.fstream(), remAuthFile->fstream(), &(*authDataEntries)[i]);
+ write_iceauth (addAuthFile.fstream(), remAuthFile->fstream(), &(*authDataEntries)[i+1]);
+
+ IceSetPaAuthData (2, &(*authDataEntries)[i]);
+
+ IceSetHostBasedAuthProc (listenObjs[i/2], HostBasedAuthProc);
+ }
+ addAuthFile.close();
+ remAuthFile->close();
+
+ QString iceAuth = KGlobal::dirs()->findExe("iceauth");
+ if (iceAuth.isEmpty())
+ {
+ qWarning("KSMServer: could not find iceauth");
+ return 0;
+ }
+
+ KProcess p;
+ p << iceAuth << "source" << addAuthFile.name();
+ p.start(KProcess::Block);
+
+ return (1);
+}
+
+/*
+ * Free up authentication data.
+ */
+void FreeAuthenticationData(int count, IceAuthDataEntry *authDataEntries)
+{
+ /* Each transport has entries for ICE and XSMP */
+ if (only_local)
+ return;
+
+ for (int i = 0; i < count * 2; i++) {
+ free (authDataEntries[i].network_id);
+ free (authDataEntries[i].auth_data);
+ }
+
+ free (authDataEntries);
+
+ QString iceAuth = KGlobal::dirs()->findExe("iceauth");
+ if (iceAuth.isEmpty())
+ {
+ qWarning("KSMServer: could not find iceauth");
+ return;
+ }
+
+ KProcess p;
+ p << iceAuth << "source" << remAuthFile->name();
+ p.start(KProcess::Block);
+
+ delete remAuthFile;
+ remAuthFile = 0;
+}
+
+static int Xio_ErrorHandler( Display * )
+{
+ qWarning("ksmserver: Fatal IO error: client killed");
+
+ // Don't do anything that might require the X connection
+ if (the_server)
+ {
+ KSMServer *server = the_server;
+ the_server = 0;
+ server->cleanUp();
+ // Don't delete server!!
+ }
+
+ exit(0); // Don't report error, it's not our fault.
+}
+
+
+void KSMServer::setupXIOErrorHandler()
+{
+ XSetIOErrorHandler(Xio_ErrorHandler);
+}
+
+static void sighandler(int sig)
+{
+ if (sig == SIGHUP) {
+ signal(SIGHUP, sighandler);
+ return;
+ }
+
+ if (the_server)
+ {
+ KSMServer *server = the_server;
+ the_server = 0;
+ server->cleanUp();
+ delete server;
+ }
+
+ if (kapp)
+ kapp->quit();
+ //::exit(0);
+}
+
+
+void KSMWatchProc ( IceConn iceConn, IcePointer client_data, Bool opening, IcePointer* watch_data)
+{
+ KSMServer* ds = ( KSMServer*) client_data;
+
+ if (opening) {
+ *watch_data = (IcePointer) ds->watchConnection( iceConn );
+ }
+ else {
+ ds->removeConnection( (KSMConnection*) *watch_data );
+ }
+}
+
+static Status KSMNewClientProc ( SmsConn conn, SmPointer manager_data,
+ unsigned long* mask_ret, SmsCallbacks* cb, char** failure_reason_ret)
+{
+ *failure_reason_ret = 0;
+
+ void* client = ((KSMServer*) manager_data )->newClient( conn );
+
+ cb->register_client.callback = KSMRegisterClientProc;
+ cb->register_client.manager_data = client;
+ cb->interact_request.callback = KSMInteractRequestProc;
+ cb->interact_request.manager_data = client;
+ cb->interact_done.callback = KSMInteractDoneProc;
+ cb->interact_done.manager_data = client;
+ cb->save_yourself_request.callback = KSMSaveYourselfRequestProc;
+ cb->save_yourself_request.manager_data = client;
+ cb->save_yourself_phase2_request.callback = KSMSaveYourselfPhase2RequestProc;
+ cb->save_yourself_phase2_request.manager_data = client;
+ cb->save_yourself_done.callback = KSMSaveYourselfDoneProc;
+ cb->save_yourself_done.manager_data = client;
+ cb->close_connection.callback = KSMCloseConnectionProc;
+ cb->close_connection.manager_data = client;
+ cb->set_properties.callback = KSMSetPropertiesProc;
+ cb->set_properties.manager_data = client;
+ cb->delete_properties.callback = KSMDeletePropertiesProc;
+ cb->delete_properties.manager_data = client;
+ cb->get_properties.callback = KSMGetPropertiesProc;
+ cb->get_properties.manager_data = client;
+
+ *mask_ret = SmsRegisterClientProcMask |
+ SmsInteractRequestProcMask |
+ SmsInteractDoneProcMask |
+ SmsSaveYourselfRequestProcMask |
+ SmsSaveYourselfP2RequestProcMask |
+ SmsSaveYourselfDoneProcMask |
+ SmsCloseConnectionProcMask |
+ SmsSetPropertiesProcMask |
+ SmsDeletePropertiesProcMask |
+ SmsGetPropertiesProcMask;
+ return 1;
+}
+
+
+#ifdef HAVE__ICETRANSNOLISTEN
+extern "C" int _IceTransNoListen(const char * protocol);
+#endif
+
+KSMServer::KSMServer( const QString& windowManager, bool _only_local )
+ : DCOPObject("ksmserver"), sessionGroup( "" )
+{
+ the_server = this;
+ clean = false;
+ wm = windowManager;
+
+ shutdownType = KApplication::ShutdownTypeNone;
+
+ state = Idle;
+ dialogActive = false;
+ saveSession = false;
+ wmPhase1WaitingCount = 0;
+ KConfig* config = KGlobal::config();
+ config->setGroup("General" );
+ clientInteracting = 0;
+ xonCommand = config->readEntry( "xonCommand", "xon" );
+
+ connect( &knotifyTimeoutTimer, SIGNAL( timeout()), SLOT( knotifyTimeout()));
+ connect( &startupSuspendTimeoutTimer, SIGNAL( timeout()), SLOT( startupSuspendTimeout()));
+ connect( &pendingShutdown, SIGNAL( timeout()), SLOT( pendingShutdownTimeout()));
+
+ only_local = _only_local;
+#ifdef HAVE__ICETRANSNOLISTEN
+ if (only_local)
+ _IceTransNoListen("tcp");
+#else
+ only_local = false;
+#endif
+
+ launcher = KApplication::launcher();
+
+ char errormsg[256];
+ if (!SmsInitialize ( (char*) KSMVendorString, (char*) KSMReleaseString,
+ KSMNewClientProc,
+ (SmPointer) this,
+ HostBasedAuthProc, 256, errormsg ) ) {
+
+ qWarning("KSMServer: could not register XSM protocol");
+ }
+
+ if (!IceListenForConnections (&numTransports, &listenObjs,
+ 256, errormsg))
+ {
+ qWarning("KSMServer: Error listening for connections: %s", errormsg);
+ qWarning("KSMServer: Aborting.");
+ exit(1);
+ }
+
+ {
+ // publish available transports.
+ QCString fName = QFile::encodeName(locateLocal("socket", "KSMserver"));
+ QCString display = ::getenv("DISPLAY");
+ // strip the screen number from the display
+ display.replace(QRegExp("\\.[0-9]+$"), "");
+ int i;
+ while( (i = display.find(':')) >= 0)
+ display[i] = '_';
+
+ fName += "_"+display;
+ FILE *f;
+ f = ::fopen(fName.data(), "w+");
+ if (!f)
+ {
+ qWarning("KSMServer: can't open %s: %s", fName.data(), strerror(errno));
+ qWarning("KSMServer: Aborting.");
+ exit(1);
+ }
+ char* session_manager = IceComposeNetworkIdList(numTransports, listenObjs);
+ fprintf(f, "%s\n%i\n", session_manager, getpid());
+ fclose(f);
+ setenv( "SESSION_MANAGER", session_manager, true );
+ // Pass env. var to kdeinit.
+ DCOPRef( launcher ).send( "setLaunchEnv", "SESSION_MANAGER", (const char*) session_manager );
+ }
+
+ if (only_local) {
+ if (!SetAuthentication_local(numTransports, listenObjs))
+ qFatal("KSMSERVER: authentication setup failed.");
+ } else {
+ if (!SetAuthentication(numTransports, listenObjs, &authDataEntries))
+ qFatal("KSMSERVER: authentication setup failed.");
+ }
+
+ IceAddConnectionWatch (KSMWatchProc, (IcePointer) this);
+
+ listener.setAutoDelete( true );
+ KSMListener* con;
+ for ( int i = 0; i < numTransports; i++) {
+ con = new KSMListener( listenObjs[i] );
+ listener.append( con );
+ connect( con, SIGNAL( activated(int) ), this, SLOT( newConnection(int) ) );
+ }
+
+ signal(SIGHUP, sighandler);
+ signal(SIGTERM, sighandler);
+ signal(SIGINT, sighandler);
+ signal(SIGPIPE, SIG_IGN);
+
+ connect( &protectionTimer, SIGNAL( timeout() ), this, SLOT( protectionTimeout() ) );
+ connect( &restoreTimer, SIGNAL( timeout() ), this, SLOT( tryRestoreNext() ) );
+ connect( kapp, SIGNAL( shutDown() ), this, SLOT( cleanUp() ) );
+}
+
+KSMServer::~KSMServer()
+{
+ the_server = 0;
+ cleanUp();
+}
+
+void KSMServer::cleanUp()
+{
+ if (clean) return;
+ clean = true;
+ IceFreeListenObjs (numTransports, listenObjs);
+
+ QCString fName = QFile::encodeName(locateLocal("socket", "KSMserver"));
+ QCString display = ::getenv("DISPLAY");
+ // strip the screen number from the display
+ display.replace(QRegExp("\\.[0-9]+$"), "");
+ int i;
+ while( (i = display.find(':')) >= 0)
+ display[i] = '_';
+
+ fName += "_"+display;
+ ::unlink(fName.data());
+
+ FreeAuthenticationData(numTransports, authDataEntries);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+
+ DM().shutdown( shutdownType, shutdownMode, bootOption );
+}
+
+
+
+void* KSMServer::watchConnection( IceConn iceConn )
+{
+ KSMConnection* conn = new KSMConnection( iceConn );
+ connect( conn, SIGNAL( activated(int) ), this, SLOT( processData(int) ) );
+ return (void*) conn;
+}
+
+void KSMServer::removeConnection( KSMConnection* conn )
+{
+ delete conn;
+}
+
+
+/*!
+ Called from our IceIoErrorHandler
+ */
+void KSMServer::ioError( IceConn /*iceConn*/ )
+{
+}
+
+void KSMServer::processData( int /*socket*/ )
+{
+ IceConn iceConn = ((KSMConnection*)sender())->iceConn;
+ IceProcessMessagesStatus status = IceProcessMessages( iceConn, 0, 0 );
+ if ( status == IceProcessMessagesIOError ) {
+ IceSetShutdownNegotiation( iceConn, False );
+ QPtrListIterator<KSMClient> it ( clients );
+ while ( it.current() &&SmsGetIceConnection( it.current()->connection() ) != iceConn )
+ ++it;
+ if ( it.current() ) {
+ SmsConn smsConn = it.current()->connection();
+ deleteClient( it.current() );
+ SmsCleanUp( smsConn );
+ }
+ (void) IceCloseConnection( iceConn );
+ }
+}
+
+KSMClient* KSMServer::newClient( SmsConn conn )
+{
+ KSMClient* client = new KSMClient( conn );
+ clients.append( client );
+ return client;
+}
+
+void KSMServer::deleteClient( KSMClient* client )
+{
+ if ( clients.findRef( client ) == -1 ) // paranoia
+ return;
+ clients.removeRef( client );
+ if ( client == clientInteracting ) {
+ clientInteracting = 0;
+ handlePendingInteractions();
+ }
+ delete client;
+ if ( state == Shutdown || state == Checkpoint )
+ completeShutdownOrCheckpoint();
+ if ( state == Killing )
+ completeKilling();
+ if ( state == KillingWM )
+ completeKillingWM();
+}
+
+void KSMServer::newConnection( int /*socket*/ )
+{
+ IceAcceptStatus status;
+ IceConn iceConn = IceAcceptConnection( ((KSMListener*)sender())->listenObj, &status);
+ IceSetShutdownNegotiation( iceConn, False );
+ IceConnectStatus cstatus;
+ while ((cstatus = IceConnectionStatus (iceConn))==IceConnectPending) {
+ (void) IceProcessMessages( iceConn, 0, 0 );
+ }
+
+ if (cstatus != IceConnectAccepted) {
+ if (cstatus == IceConnectIOError)
+ kdDebug( 1218 ) << "IO error opening ICE Connection!" << endl;
+ else
+ kdDebug( 1218 ) << "ICE Connection rejected!" << endl;
+ (void )IceCloseConnection (iceConn);
+ }
+}
+
+
+QString KSMServer::currentSession()
+{
+ if ( sessionGroup.startsWith( "Session: " ) )
+ return sessionGroup.mid( 9 );
+ return ""; // empty, not null, since used for KConfig::setGroup
+}
+
+void KSMServer::discardSession()
+{
+ KConfig* config = KGlobal::config();
+ config->setGroup( sessionGroup );
+ int count = config->readNumEntry( "count", 0 );
+ for ( KSMClient* c = clients.first(); c; c = clients.next() ) {
+ QStringList discardCommand = c->discardCommand();
+ if ( discardCommand.isEmpty())
+ continue;
+ // check that non of the old clients used the exactly same
+ // discardCommand before we execute it. This used to be the
+ // case up to KDE and Qt < 3.1
+ int i = 1;
+ while ( i <= count &&
+ config->readPathListEntry( QString("discardCommand") + QString::number(i) ) != discardCommand )
+ i++;
+ if ( i <= count )
+ executeCommand( discardCommand );
+ }
+}
+
+void KSMServer::storeSession()
+{
+ KConfig* config = KGlobal::config();
+ config->reparseConfiguration(); // config may have changed in the KControl module
+ config->setGroup("General" );
+ excludeApps = QStringList::split( QRegExp( "[,:]" ), config->readEntry( "excludeApps" ).lower());
+ config->setGroup( sessionGroup );
+ int count = config->readNumEntry( "count" );
+ for ( int i = 1; i <= count; i++ ) {
+ QStringList discardCommand = config->readPathListEntry( QString("discardCommand") + QString::number(i) );
+ if ( discardCommand.isEmpty())
+ continue;
+ // check that non of the new clients uses the exactly same
+ // discardCommand before we execute it. This used to be the
+ // case up to KDE and Qt < 3.1
+ KSMClient* c = clients.first();
+ while ( c && discardCommand != c->discardCommand() )
+ c = clients.next();
+ if ( c )
+ continue;
+ executeCommand( discardCommand );
+ }
+ config->deleteGroup( sessionGroup ); //### does not work with global config object...
+ config->setGroup( sessionGroup );
+ count = 0;
+
+ if ( !wm.isEmpty() ) {
+ // put the wm first
+ for ( KSMClient* c = clients.first(); c; c = clients.next() )
+ if ( c->program() == wm ) {
+ clients.prepend( clients.take() );
+ break;
+ }
+ }
+
+ for ( KSMClient* c = clients.first(); c; c = clients.next() ) {
+ int restartHint = c->restartStyleHint();
+ if (restartHint == SmRestartNever)
+ continue;
+ QString program = c->program();
+ QStringList restartCommand = c->restartCommand();
+ if (program.isEmpty() && restartCommand.isEmpty())
+ continue;
+ if (excludeApps.contains( program.lower()))
+ continue;
+
+ count++;
+ QString n = QString::number(count);
+ config->writeEntry( QString("program")+n, program );
+ config->writeEntry( QString("clientId")+n, c->clientId() );
+ config->writeEntry( QString("restartCommand")+n, restartCommand );
+ config->writePathEntry( QString("discardCommand")+n, c->discardCommand() );
+ config->writeEntry( QString("restartStyleHint")+n, restartHint );
+ config->writeEntry( QString("userId")+n, c->userId() );
+ config->writeEntry( QString("wasWm")+n, isWM( c ));
+ }
+ config->writeEntry( "count", count );
+
+ config->setGroup("General");
+ config->writeEntry( "screenCount", ScreenCount(qt_xdisplay()));
+
+ storeLegacySession( config );
+ config->sync();
+}
+
+
+QStringList KSMServer::sessionList()
+{
+ QStringList sessions = "default";
+ KConfig* config = KGlobal::config();
+ QStringList groups = config->groupList();
+ for ( QStringList::ConstIterator it = groups.begin(); it != groups.end(); it++ )
+ if ( (*it).startsWith( "Session: " ) )
+ sessions << (*it).mid( 9 );
+ return sessions;
+}
+
+bool KSMServer::isWM( const KSMClient* client ) const
+{
+ return isWM( client->program());
+}
+
+bool KSMServer::isWM( const QString& program ) const
+{
+ // KWin relies on ksmserver's special treatment in phase1,
+ // therefore make sure it's recognized even if ksmserver
+ // was initially started with different WM, and kwin replaced
+ // it later
+ return program == wm || program == "kwin";
+}
+
+bool KSMServer::defaultSession() const
+{
+ return sessionGroup.isEmpty();
+}