add more spacing
[personal-kdebase.git] / workspace / ksmserver / server.cpp
blobf14d299af2e187ce20525c55846da583376ef7d0
1 /*****************************************************************
2 ksmserver - the KDE session management server
4 Copyright 2000 Matthias Ettrich <ettrich@kde.org>
5 Copyright 2005 Lubos Lunak <l.lunak@kde.org>
7 relatively small extensions by Oswald Buddenhagen <ob6@inf.tu-dresden.de>
9 some code taken from the dcopserver (part of the KDE libraries), which is
10 Copyright 1999 Matthias Ettrich <ettrich@kde.org>
11 Copyright 1999 Preston Brown <pbrown@kde.org>
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
20 The above copyright notice and this permission notice shall be included in
21 all copies or substantial portions of the Software.
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
27 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
28 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 ******************************************************************/
32 #include "server.h"
33 #include "global.h"
34 #include "client.h"
35 #include "ksmserverinterfaceadaptor.h"
37 #include <config-workspace.h>
38 #include <config-unix.h> // HAVE_LIMITS_H
39 #include <config-ksmserver.h>
40 #include <pwd.h>
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/stat.h>
44 #ifdef HAVE_SYS_TIME_H
45 #include <sys/time.h>
46 #endif
47 #include <sys/socket.h>
48 #include <sys/un.h>
50 #include <unistd.h>
51 #include <stdlib.h>
52 #include <signal.h>
53 #include <time.h>
54 #include <errno.h>
55 #include <string.h>
56 #include <assert.h>
57 #include <fcntl.h>
58 #include <kdefakes.h>
60 #ifdef HAVE_LIMITS_H
61 #include <limits.h>
62 #endif
64 #include <QFile>
65 #include <QPushButton>
66 #include <QRegExp>
67 #include <QtDBus/QtDBus>
68 #include <QSocketNotifier>
70 #include <klocale.h>
71 #include <kglobal.h>
72 #include <kconfig.h>
73 #include <kdesktopfile.h>
74 #include <kstandarddirs.h>
75 #include <kapplication.h>
76 #include <ktemporaryfile.h>
77 #include <kconfiggroup.h>
78 #include <kprocess.h>
79 #include <kdebug.h>
80 #include <kshell.h>
81 #include <kprocess.h>
83 #include "server.moc"
85 #include <kdisplaymanager.h>
86 #include <QX11Info>
87 #include <krandom.h>
88 #include "klauncher_interface.h"
90 KSMServer* the_server = 0;
92 KSMServer* KSMServer::self()
94 return the_server;
97 /*! Utility function to execute a command on the local machine. Used
98 * to restart applications.
100 KProcess* KSMServer::startApplication( const QStringList& cmd, const QString& clientMachine,
101 const QString& userId )
103 QStringList command = cmd;
104 if ( command.isEmpty() )
105 return NULL;
106 if ( !userId.isEmpty()) {
107 struct passwd* pw = getpwuid( getuid());
108 if( pw != NULL && userId != QString::fromLocal8Bit( pw->pw_name )) {
109 command.prepend( "--" );
110 command.prepend( userId );
111 command.prepend( "-u" );
112 command.prepend( KStandardDirs::findExe("kdesu") );
115 if ( !clientMachine.isEmpty() && clientMachine != "localhost" ) {
116 command.prepend( clientMachine );
117 command.prepend( xonCommand ); // "xon" by default
119 KProcess* process = new KProcess( this );
120 *process << command;
121 // make it auto-delete
122 connect( process, SIGNAL( error( QProcess::ProcessError )), process, SLOT( deleteLater()));
123 connect( process, SIGNAL( finished( int, QProcess::ExitStatus )), process, SLOT( deleteLater()));
124 process->start();
125 return process;
128 /*! Utility function to execute a command on the local machine. Used
129 * to discard session data
131 void KSMServer::executeCommand( const QStringList& command )
133 if ( command.isEmpty() )
134 return;
136 KProcess::execute( command );
139 IceAuthDataEntry *authDataEntries = 0;
141 static KTemporaryFile *remTempFile = 0;
143 static IceListenObj *listenObjs = 0;
144 int numTransports = 0;
145 static bool only_local = 0;
147 static Bool HostBasedAuthProc ( char* /*hostname*/)
149 if (only_local)
150 return true;
151 else
152 return false;
156 Status KSMRegisterClientProc (
157 SmsConn /* smsConn */,
158 SmPointer managerData,
159 char * previousId
162 KSMClient* client = (KSMClient*) managerData;
163 client->registerClient( previousId );
164 return 1;
167 void KSMInteractRequestProc (
168 SmsConn /* smsConn */,
169 SmPointer managerData,
170 int dialogType
173 the_server->interactRequest( (KSMClient*) managerData, dialogType );
176 void KSMInteractDoneProc (
177 SmsConn /* smsConn */,
178 SmPointer managerData,
179 Bool cancelShutdown
182 the_server->interactDone( (KSMClient*) managerData, cancelShutdown );
185 void KSMSaveYourselfRequestProc (
186 SmsConn smsConn ,
187 SmPointer /* managerData */,
188 int saveType,
189 Bool shutdown,
190 int interactStyle,
191 Bool fast,
192 Bool global
195 if ( shutdown ) {
196 the_server->shutdown( fast ?
197 KWorkSpace::ShutdownConfirmNo :
198 KWorkSpace::ShutdownConfirmDefault,
199 KWorkSpace::ShutdownTypeDefault,
200 KWorkSpace::ShutdownModeDefault );
201 } else if ( !global ) {
202 SmsSaveYourself( smsConn, saveType, false, interactStyle, fast );
203 SmsSaveComplete( smsConn );
205 // else checkpoint only, ksmserver does not yet support this
206 // mode. Will come for KDE 3.1
209 void KSMSaveYourselfPhase2RequestProc (
210 SmsConn /* smsConn */,
211 SmPointer managerData
214 the_server->phase2Request( (KSMClient*) managerData );
217 void KSMSaveYourselfDoneProc (
218 SmsConn /* smsConn */,
219 SmPointer managerData,
220 Bool success
223 the_server->saveYourselfDone( (KSMClient*) managerData, success );
226 void KSMCloseConnectionProc (
227 SmsConn smsConn,
228 SmPointer managerData,
229 int count,
230 char ** reasonMsgs
233 the_server->deleteClient( ( KSMClient* ) managerData );
234 if ( count )
235 SmFreeReasons( count, reasonMsgs );
236 IceConn iceConn = SmsGetIceConnection( smsConn );
237 SmsCleanUp( smsConn );
238 IceSetShutdownNegotiation (iceConn, False);
239 IceCloseConnection( iceConn );
242 void KSMSetPropertiesProc (
243 SmsConn /* smsConn */,
244 SmPointer managerData,
245 int numProps,
246 SmProp ** props
249 KSMClient* client = ( KSMClient* ) managerData;
250 for ( int i = 0; i < numProps; i++ ) {
251 SmProp *p = client->property( props[i]->name );
252 if ( p ) {
253 client->properties.removeAll( p );
254 SmFreeProperty( p );
256 client->properties.append( props[i] );
257 if ( !qstrcmp( props[i]->name, SmProgram ) )
258 the_server->clientSetProgram( client );
261 if ( numProps )
262 free( props );
266 void KSMDeletePropertiesProc (
267 SmsConn /* smsConn */,
268 SmPointer managerData,
269 int numProps,
270 char ** propNames
273 KSMClient* client = ( KSMClient* ) managerData;
274 for ( int i = 0; i < numProps; i++ ) {
275 SmProp *p = client->property( propNames[i] );
276 if ( p ) {
277 client->properties.removeAll( p );
278 SmFreeProperty( p );
283 void KSMGetPropertiesProc (
284 SmsConn smsConn,
285 SmPointer managerData
288 KSMClient* client = ( KSMClient* ) managerData;
289 SmProp** props = new SmProp*[client->properties.count()];
290 int i = 0;
291 foreach( SmProp *prop, client->properties )
292 props[i++] = prop;
294 SmsReturnProperties( smsConn, i, props );
295 delete [] props;
299 class KSMListener : public QSocketNotifier
301 public:
302 KSMListener( IceListenObj obj )
303 : QSocketNotifier( IceGetListenConnectionNumber( obj ),
304 QSocketNotifier::Read )
306 listenObj = obj;
309 IceListenObj listenObj;
312 class KSMConnection : public QSocketNotifier
314 public:
315 KSMConnection( IceConn conn )
316 : QSocketNotifier( IceConnectionNumber( conn ),
317 QSocketNotifier::Read )
319 iceConn = conn;
322 IceConn iceConn;
326 /* for printing hex digits */
327 static void fprintfhex (FILE *fp, unsigned int len, char *cp)
329 static const char hexchars[] = "0123456789abcdef";
331 for (; len > 0; len--, cp++) {
332 unsigned char s = *cp;
333 putc(hexchars[s >> 4], fp);
334 putc(hexchars[s & 0x0f], fp);
339 * We use temporary files which contain commands to add/remove entries from
340 * the .ICEauthority file.
342 static void write_iceauth (FILE *addfp, FILE *removefp, IceAuthDataEntry *entry)
344 fprintf (addfp,
345 "add %s \"\" %s %s ",
346 entry->protocol_name,
347 entry->network_id,
348 entry->auth_name);
349 fprintfhex (addfp, entry->auth_data_length, entry->auth_data);
350 fprintf (addfp, "\n");
352 fprintf (removefp,
353 "remove protoname=%s protodata=\"\" netid=%s authname=%s\n",
354 entry->protocol_name,
355 entry->network_id,
356 entry->auth_name);
360 #define MAGIC_COOKIE_LEN 16
362 Status SetAuthentication_local (int count, IceListenObj *listenObjs)
364 int i;
365 for (i = 0; i < count; i ++) {
366 char *prot = IceGetListenConnectionString(listenObjs[i]);
367 if (!prot) continue;
368 char *host = strchr(prot, '/');
369 char *sock = 0;
370 if (host) {
371 *host=0;
372 host++;
373 sock = strchr(host, ':');
374 if (sock) {
375 *sock = 0;
376 sock++;
379 kDebug( 1218 ) << "KSMServer: SetAProc_loc: conn " << (unsigned)i << ", prot=" << prot << ", file=" << sock;
380 if (sock && !strcmp(prot, "local")) {
381 chmod(sock, 0700);
383 IceSetHostBasedAuthProc (listenObjs[i], HostBasedAuthProc);
384 free(prot);
386 return 1;
389 Status SetAuthentication (int count, IceListenObj *listenObjs,
390 IceAuthDataEntry **authDataEntries)
392 KTemporaryFile addTempFile;
393 remTempFile = new KTemporaryFile;
395 if (!addTempFile.open() || !remTempFile->open())
396 return 0;
398 if ((*authDataEntries = (IceAuthDataEntry *) malloc (
399 count * 2 * sizeof (IceAuthDataEntry))) == NULL)
400 return 0;
402 FILE *addAuthFile = fopen(QFile::encodeName(addTempFile.fileName()), "r+");
403 FILE *remAuthFile = fopen(QFile::encodeName(remTempFile->fileName()), "r+");
405 for (int i = 0; i < numTransports * 2; i += 2) {
406 (*authDataEntries)[i].network_id =
407 IceGetListenConnectionString (listenObjs[i/2]);
408 (*authDataEntries)[i].protocol_name = (char *) "ICE";
409 (*authDataEntries)[i].auth_name = (char *) "MIT-MAGIC-COOKIE-1";
411 (*authDataEntries)[i].auth_data =
412 IceGenerateMagicCookie (MAGIC_COOKIE_LEN);
413 (*authDataEntries)[i].auth_data_length = MAGIC_COOKIE_LEN;
415 (*authDataEntries)[i+1].network_id =
416 IceGetListenConnectionString (listenObjs[i/2]);
417 (*authDataEntries)[i+1].protocol_name = (char *) "XSMP";
418 (*authDataEntries)[i+1].auth_name = (char *) "MIT-MAGIC-COOKIE-1";
420 (*authDataEntries)[i+1].auth_data =
421 IceGenerateMagicCookie (MAGIC_COOKIE_LEN);
422 (*authDataEntries)[i+1].auth_data_length = MAGIC_COOKIE_LEN;
424 write_iceauth (addAuthFile, remAuthFile, &(*authDataEntries)[i]);
425 write_iceauth (addAuthFile, remAuthFile, &(*authDataEntries)[i+1]);
427 IceSetPaAuthData (2, &(*authDataEntries)[i]);
429 IceSetHostBasedAuthProc (listenObjs[i/2], HostBasedAuthProc);
431 fclose(addAuthFile);
432 fclose(remAuthFile);
434 QString iceAuth = KGlobal::dirs()->findExe("iceauth");
435 if (iceAuth.isEmpty())
437 qWarning("KSMServer: could not find iceauth");
438 return 0;
441 KProcess p;
442 p << iceAuth << "source" << addTempFile.fileName();
443 p.execute();
445 return (1);
449 * Free up authentication data.
451 void FreeAuthenticationData(int count, IceAuthDataEntry *authDataEntries)
453 /* Each transport has entries for ICE and XSMP */
454 if (only_local)
455 return;
457 for (int i = 0; i < count * 2; i++) {
458 free (authDataEntries[i].network_id);
459 free (authDataEntries[i].auth_data);
462 free (authDataEntries);
464 QString iceAuth = KGlobal::dirs()->findExe("iceauth");
465 if (iceAuth.isEmpty())
467 qWarning("KSMServer: could not find iceauth");
468 return;
471 if (remTempFile)
473 KProcess p;
474 p << iceAuth << "source" << remTempFile->fileName();
475 p.execute();
478 delete remTempFile;
479 remTempFile = 0;
482 static int Xio_ErrorHandler( Display * )
484 qWarning("ksmserver: Fatal IO error: client killed");
486 // Don't do anything that might require the X connection
487 if (the_server)
489 KSMServer *server = the_server;
490 the_server = 0;
491 server->cleanUp();
492 // Don't delete server!!
495 exit(0); // Don't report error, it's not our fault.
496 return 0; // Bogus return value, notreached
499 void KSMServer::setupXIOErrorHandler()
501 XSetIOErrorHandler(Xio_ErrorHandler);
504 static void sighandler(int sig)
506 if (sig == SIGHUP) {
507 signal(SIGHUP, sighandler);
508 return;
511 if (the_server)
513 KSMServer *server = the_server;
514 the_server = 0;
515 server->cleanUp();
516 delete server;
519 if (kapp)
520 kapp->quit();
521 //::exit(0);
525 void KSMWatchProc ( IceConn iceConn, IcePointer client_data, Bool opening, IcePointer* watch_data)
527 KSMServer* ds = ( KSMServer*) client_data;
529 if (opening) {
530 *watch_data = (IcePointer) ds->watchConnection( iceConn );
532 else {
533 ds->removeConnection( (KSMConnection*) *watch_data );
537 static Status KSMNewClientProc ( SmsConn conn, SmPointer manager_data,
538 unsigned long* mask_ret, SmsCallbacks* cb, char** failure_reason_ret)
540 *failure_reason_ret = 0;
542 void* client = ((KSMServer*) manager_data )->newClient( conn );
544 cb->register_client.callback = KSMRegisterClientProc;
545 cb->register_client.manager_data = client;
546 cb->interact_request.callback = KSMInteractRequestProc;
547 cb->interact_request.manager_data = client;
548 cb->interact_done.callback = KSMInteractDoneProc;
549 cb->interact_done.manager_data = client;
550 cb->save_yourself_request.callback = KSMSaveYourselfRequestProc;
551 cb->save_yourself_request.manager_data = client;
552 cb->save_yourself_phase2_request.callback = KSMSaveYourselfPhase2RequestProc;
553 cb->save_yourself_phase2_request.manager_data = client;
554 cb->save_yourself_done.callback = KSMSaveYourselfDoneProc;
555 cb->save_yourself_done.manager_data = client;
556 cb->close_connection.callback = KSMCloseConnectionProc;
557 cb->close_connection.manager_data = client;
558 cb->set_properties.callback = KSMSetPropertiesProc;
559 cb->set_properties.manager_data = client;
560 cb->delete_properties.callback = KSMDeletePropertiesProc;
561 cb->delete_properties.manager_data = client;
562 cb->get_properties.callback = KSMGetPropertiesProc;
563 cb->get_properties.manager_data = client;
565 *mask_ret = SmsRegisterClientProcMask |
566 SmsInteractRequestProcMask |
567 SmsInteractDoneProcMask |
568 SmsSaveYourselfRequestProcMask |
569 SmsSaveYourselfP2RequestProcMask |
570 SmsSaveYourselfDoneProcMask |
571 SmsCloseConnectionProcMask |
572 SmsSetPropertiesProcMask |
573 SmsDeletePropertiesProcMask |
574 SmsGetPropertiesProcMask;
575 return 1;
579 #ifdef HAVE__ICETRANSNOLISTEN
580 extern "C" int _IceTransNoListen(const char * protocol);
581 #endif
583 KSMServer::KSMServer( const QString& windowManager, bool _only_local )
584 : wmProcess( NULL )
585 , sessionGroup( "" )
586 , logoutEffectWidget( NULL )
588 new KSMServerInterfaceAdaptor( this );
589 QDBusConnection::sessionBus().registerObject("/KSMServer", this);
590 klauncherSignals = new OrgKdeKLauncherInterface(QLatin1String("org.kde.klauncher"),
591 QLatin1String("/KLauncher"), QDBusConnection::sessionBus());
592 kcminitSignals = NULL;
593 the_server = this;
594 clean = false;
596 shutdownType = KWorkSpace::ShutdownTypeNone;
598 state = Idle;
599 dialogActive = false;
600 saveSession = false;
601 wmPhase1WaitingCount = 0;
602 KConfigGroup config(KGlobal::config(), "General");
603 clientInteracting = 0;
604 xonCommand = config.readEntry( "xonCommand", "xon" );
606 KGlobal::dirs()->addResourceType( "windowmanagers", "data", "ksmserver/windowmanagers" );
607 selectWm( windowManager );
609 connect( &startupSuspendTimeoutTimer, SIGNAL( timeout()), SLOT( startupSuspendTimeout()));
610 connect( &pendingShutdown, SIGNAL( timeout()), SLOT( pendingShutdownTimeout()));
612 only_local = _only_local;
613 #ifdef HAVE__ICETRANSNOLISTEN
614 if (only_local)
615 _IceTransNoListen("tcp");
616 #else
617 only_local = false;
618 #endif
620 char errormsg[256];
621 if (!SmsInitialize ( (char*) KSMVendorString, (char*) KSMReleaseString,
622 KSMNewClientProc,
623 (SmPointer) this,
624 HostBasedAuthProc, 256, errormsg ) ) {
626 qWarning("KSMServer: could not register XSM protocol");
629 if (!IceListenForConnections (&numTransports, &listenObjs,
630 256, errormsg))
632 qWarning("KSMServer: Error listening for connections: %s", errormsg);
633 qWarning("KSMServer: Aborting.");
634 exit(1);
638 // publish available transports.
639 QByteArray fName = QFile::encodeName(KStandardDirs::locateLocal("socket", "KSMserver"));
640 QString display = ::getenv("DISPLAY");
641 // strip the screen number from the display
642 display.replace(QRegExp("\\.[0-9]+$"), "");
643 int i;
644 while( (i = display.indexOf(':')) >= 0)
645 display[i] = '_';
647 fName += '_'+display.toLocal8Bit();
648 FILE *f;
649 f = ::fopen(fName.data(), "w+");
650 if (!f)
652 qWarning("KSMServer: cannot open %s: %s", fName.data(), strerror(errno));
653 qWarning("KSMServer: Aborting.");
654 exit(1);
656 char* session_manager = IceComposeNetworkIdList(numTransports, listenObjs);
657 fprintf(f, "%s\n%i\n", session_manager, getpid());
658 fclose(f);
659 setenv( "SESSION_MANAGER", session_manager, true );
660 // Pass env. var to kdeinit.
661 org::kde::KLauncher klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
662 klauncher.setLaunchEnv( "SESSION_MANAGER", (const char*) session_manager );
665 if (only_local) {
666 if (!SetAuthentication_local(numTransports, listenObjs))
667 qFatal("KSMSERVER: authentication setup failed.");
668 } else {
669 if (!SetAuthentication(numTransports, listenObjs, &authDataEntries))
670 qFatal("KSMSERVER: authentication setup failed.");
673 IceAddConnectionWatch (KSMWatchProc, (IcePointer) this);
675 KSMListener* con;
676 for ( int i = 0; i < numTransports; i++) {
677 fcntl( IceGetListenConnectionNumber( listenObjs[i] ), F_SETFD, FD_CLOEXEC );
678 con = new KSMListener( listenObjs[i] );
679 listener.append( con );
680 connect( con, SIGNAL( activated(int) ), this, SLOT( newConnection(int) ) );
683 signal(SIGHUP, sighandler);
684 signal(SIGTERM, sighandler);
685 signal(SIGINT, sighandler);
686 signal(SIGPIPE, SIG_IGN);
688 connect( &protectionTimer, SIGNAL( timeout() ), this, SLOT( protectionTimeout() ) );
689 connect( &restoreTimer, SIGNAL( timeout() ), this, SLOT( tryRestoreNext() ) );
690 connect( qApp, SIGNAL( aboutToQuit() ), this, SLOT( cleanUp() ) );
693 KSMServer::~KSMServer()
695 qDeleteAll( listener );
696 the_server = 0;
697 cleanUp();
700 void KSMServer::cleanUp()
702 if (clean) return;
703 clean = true;
704 IceFreeListenObjs (numTransports, listenObjs);
706 QByteArray fName = QFile::encodeName(KStandardDirs::locateLocal("socket", "KSMserver"));
707 QString display = QString::fromLocal8Bit(::getenv("DISPLAY"));
708 // strip the screen number from the display
709 display.replace(QRegExp("\\.[0-9]+$"), "");
710 int i;
711 while( (i = display.indexOf(':')) >= 0)
712 display[i] = '_';
714 fName += '_'+display.toLocal8Bit();
715 ::unlink(fName.data());
717 FreeAuthenticationData(numTransports, authDataEntries);
718 signal(SIGTERM, SIG_DFL);
719 signal(SIGINT, SIG_DFL);
721 KDisplayManager().shutdown( shutdownType, shutdownMode, bootOption );
726 void* KSMServer::watchConnection( IceConn iceConn )
728 KSMConnection* conn = new KSMConnection( iceConn );
729 connect( conn, SIGNAL( activated(int) ), this, SLOT( processData(int) ) );
730 return (void*) conn;
733 void KSMServer::removeConnection( KSMConnection* conn )
735 delete conn;
740 Called from our IceIoErrorHandler
742 void KSMServer::ioError( IceConn /*iceConn*/ )
746 void KSMServer::processData( int /*socket*/ )
748 IceConn iceConn = ((KSMConnection*)sender())->iceConn;
749 IceProcessMessagesStatus status = IceProcessMessages( iceConn, 0, 0 );
750 if ( status == IceProcessMessagesIOError ) {
751 IceSetShutdownNegotiation( iceConn, False );
752 QList<KSMClient*>::iterator it = clients.begin();
753 QList<KSMClient*>::iterator const itEnd = clients.end();
754 while ( ( it != itEnd ) && *it && ( SmsGetIceConnection( ( *it )->connection() ) != iceConn ) )
755 ++it;
756 if ( ( it != itEnd ) && *it ) {
757 SmsConn smsConn = (*it)->connection();
758 deleteClient( *it );
759 SmsCleanUp( smsConn );
761 (void) IceCloseConnection( iceConn );
765 KSMClient* KSMServer::newClient( SmsConn conn )
767 KSMClient* client = new KSMClient( conn );
768 clients.append( client );
769 return client;
772 void KSMServer::deleteClient( KSMClient* client )
774 if ( !clients.contains( client ) ) // paranoia
775 return;
776 clients.removeAll( client );
777 if ( client == clientInteracting ) {
778 clientInteracting = 0;
779 handlePendingInteractions();
781 delete client;
782 if ( state == Shutdown || state == Checkpoint )
783 completeShutdownOrCheckpoint();
784 if ( state == Killing )
785 completeKilling();
786 if ( state == KillingWM )
787 completeKillingWM();
790 void KSMServer::newConnection( int /*socket*/ )
792 IceAcceptStatus status;
793 IceConn iceConn = IceAcceptConnection( ((KSMListener*)sender())->listenObj, &status);
794 IceSetShutdownNegotiation( iceConn, False );
795 IceConnectStatus cstatus;
796 while ((cstatus = IceConnectionStatus (iceConn))==IceConnectPending) {
797 (void) IceProcessMessages( iceConn, 0, 0 );
800 if (cstatus != IceConnectAccepted) {
801 if (cstatus == IceConnectIOError)
802 kDebug( 1218 ) << "IO error opening ICE Connection!";
803 else
804 kDebug( 1218 ) << "ICE Connection rejected!";
805 (void )IceCloseConnection (iceConn);
808 // don't leak the fd
809 fcntl( IceConnectionNumber(iceConn), F_SETFD, FD_CLOEXEC );
813 QString KSMServer::currentSession()
815 if ( sessionGroup.startsWith( "Session: " ) )
816 return sessionGroup.mid( 9 );
817 return ""; // empty, not null, since used for KConfig::setGroup
820 void KSMServer::discardSession()
822 KConfigGroup config(KGlobal::config(), sessionGroup );
823 int count = config.readEntry( "count", 0 );
824 foreach ( KSMClient *c, clients ) {
825 QStringList discardCommand = c->discardCommand();
826 if ( discardCommand.isEmpty())
827 continue;
828 // check that non of the old clients used the exactly same
829 // discardCommand before we execute it. This used to be the
830 // case up to KDE and Qt < 3.1
831 int i = 1;
832 while ( i <= count &&
833 config.readPathEntry( QString("discardCommand") + QString::number(i), QStringList() ) != discardCommand )
834 i++;
835 if ( i <= count )
836 executeCommand( discardCommand );
840 void KSMServer::storeSession()
842 KSharedConfig::Ptr config = KGlobal::config();
843 config->reparseConfiguration(); // config may have changed in the KControl module
844 KConfigGroup generalGroup(config, "General");
845 excludeApps = generalGroup.readEntry( "excludeApps" ).toLower().split( QRegExp( "[,:]" ), QString::SkipEmptyParts );
846 KConfigGroup configSessionGroup(config, sessionGroup);
847 int count = configSessionGroup.readEntry( "count", 0 );
848 for ( int i = 1; i <= count; i++ ) {
849 QStringList discardCommand = configSessionGroup.readPathEntry( QLatin1String("discardCommand") + QString::number(i), QStringList() );
850 if ( discardCommand.isEmpty())
851 continue;
852 // check that non of the new clients uses the exactly same
853 // discardCommand before we execute it. This used to be the
854 // case up to KDE and Qt < 3.1
855 QList<KSMClient*>::iterator it = clients.begin();
856 QList<KSMClient*>::iterator const itEnd = clients.end();
857 while ( ( it != itEnd ) && *it && (discardCommand != ( *it )->discardCommand() ) )
858 ++it;
859 if ( ( it != itEnd ) && *it )
860 continue;
861 executeCommand( discardCommand );
863 config->deleteGroup( sessionGroup ); //### does not work with global config object...
864 KConfigGroup cg( config, sessionGroup);
865 count = 0;
867 // put the wm first
868 foreach ( KSMClient *c, clients )
869 if ( c->program() == wm ) {
870 clients.removeAll( c );
871 clients.prepend( c );
872 break;
875 foreach ( KSMClient *c, clients ) {
876 int restartHint = c->restartStyleHint();
877 if (restartHint == SmRestartNever)
878 continue;
879 QString program = c->program();
880 QStringList restartCommand = c->restartCommand();
881 if (program.isEmpty() && restartCommand.isEmpty())
882 continue;
883 if (excludeApps.contains( program.toLower()))
884 continue;
886 count++;
887 QString n = QString::number(count);
888 cg.writeEntry( QString("program")+n, program );
889 cg.writeEntry( QString("clientId")+n, c->clientId() );
890 cg.writeEntry( QString("restartCommand")+n, restartCommand );
891 cg.writePathEntry( QString("discardCommand")+n, c->discardCommand() );
892 cg.writeEntry( QString("restartStyleHint")+n, restartHint );
893 cg.writeEntry( QString("userId")+n, c->userId() );
894 cg.writeEntry( QString("wasWm")+n, isWM( c ));
896 cg.writeEntry( "count", count );
898 KConfigGroup cg2( config, "General");
899 cg2.writeEntry( "screenCount", ScreenCount(QX11Info::display()));
901 storeLegacySession(config.data());
902 config->sync();
905 QStringList KSMServer::sessionList()
907 QStringList sessions ( "default" );
908 KSharedConfig::Ptr config = KGlobal::config();
909 const QStringList groups = config->groupList();
910 for ( QStringList::ConstIterator it = groups.constBegin(); it != groups.constEnd(); it++ )
911 if ( (*it).startsWith( "Session: " ) )
912 sessions << (*it).mid( 9 );
913 return sessions;
916 bool KSMServer::isWM( const KSMClient* client ) const
918 return isWM( client->program());
921 bool KSMServer::isWM( const QString& command ) const
923 return command == wm;
926 bool KSMServer::defaultSession() const
928 return sessionGroup.isEmpty();
931 // selection logic:
932 // - $KDEWM is set - use that
933 // - a wm is selected using the kcm - use that
934 // - if that fails, just use KWin
935 void KSMServer::selectWm( const QString& kdewm )
937 wm = "kwin"; // defaults
938 wmCommands = ( QStringList() << "kwin" );
939 if( qstrcmp( getenv( "KDE_FAILSAFE" ), "1" ) == 0 )
940 return; // failsafe, force kwin
941 if( !kdewm.isEmpty())
943 wmCommands = ( QStringList() << kdewm );
944 wm = kdewm;
945 return;
947 KConfigGroup config(KGlobal::config(), "General");
948 QString cfgwm = config.readEntry( "windowManager", "kwin" );
949 KDesktopFile file( "windowmanagers", cfgwm + ".desktop" );
950 if( file.noDisplay())
951 return;
952 if( !file.tryExec())
953 return;
954 QString testexec = file.desktopGroup().readEntry( "X-KDE-WindowManagerTestExec" );
955 if( !testexec.isEmpty())
957 KProcess proc;
958 proc.setShellCommand( testexec );
959 if( proc.execute() != 0 )
960 return;
962 QStringList cfgWmCommands = KShell::splitArgs( file.desktopGroup().readEntry( "Exec" ));
963 if( cfgWmCommands.isEmpty())
964 return;
965 QString smname = file.desktopGroup().readEntry( "X-KDE-WindowManagerId" );
966 // ok
967 wm = smname.isEmpty() ? cfgwm : smname;
968 wmCommands = cfgWmCommands;
971 void KSMServer::wmChanged()
973 KGlobal::config()->reparseConfiguration();
974 selectWm( "" );