~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to ksmserver/server.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*****************************************************************
 
2
ksmserver - the KDE session management server
 
3
 
 
4
Copyright 2000 Matthias Ettrich <ettrich@kde.org>
 
5
Copyright 2005 Lubos Lunak <l.lunak@kde.org>
 
6
 
 
7
relatively small extensions by Oswald Buddenhagen <ob6@inf.tu-dresden.de>
 
8
 
 
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>
 
12
 
 
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:
 
19
 
 
20
The above copyright notice and this permission notice shall be included in
 
21
all copies or substantial portions of the Software.
 
22
 
 
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.
 
29
 
 
30
******************************************************************/
 
31
 
 
32
#include "server.h"
 
33
#include "global.h"
 
34
#include "client.h"
 
35
#include "ksmserverinterfaceadaptor.h"
 
36
 
 
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>
 
49
 
 
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>
 
59
 
 
60
#ifdef HAVE_LIMITS_H
 
61
#include <limits.h>
 
62
#endif
 
63
 
 
64
#include <QFile>
 
65
#include <QPushButton>
 
66
#include <QRegExp>
 
67
#include <QtDBus/QtDBus>
 
68
#include <QSocketNotifier>
 
69
 
 
70
#include <kaction.h>
 
71
#include <kactioncollection.h>
 
72
#include <kauthorized.h>
 
73
#include <klocale.h>
 
74
#include <kglobal.h>
 
75
#include <kconfig.h>
 
76
#include <kdesktopfile.h>
 
77
#include <kstandarddirs.h>
 
78
#include <kapplication.h>
 
79
#include <ktemporaryfile.h>
 
80
#include <kconfiggroup.h>
 
81
#include <kprocess.h>
 
82
#include <kdebug.h>
 
83
#include <kshell.h>
 
84
 
 
85
#include "server.moc"
 
86
 
 
87
#include <kdisplaymanager.h>
 
88
#include <QX11Info>
 
89
#include <krandom.h>
 
90
#include <klauncher_iface.h>
 
91
 
 
92
KSMServer* the_server = 0;
 
93
 
 
94
KSMServer* KSMServer::self()
 
95
{
 
96
    return the_server;
 
97
}
 
98
 
 
99
/*! Utility function to execute a command on the local machine. Used
 
100
 * to restart applications.
 
101
 */
 
102
KProcess* KSMServer::startApplication( const QStringList& cmd, const QString& clientMachine,
 
103
    const QString& userId, bool wm )
 
104
{
 
105
    QStringList command = cmd;
 
106
    if ( command.isEmpty() )
 
107
        return NULL;
 
108
    if ( !userId.isEmpty()) {
 
109
        struct passwd* pw = getpwuid( getuid());
 
110
        if( pw != NULL && userId != QString::fromLocal8Bit( pw->pw_name )) {
 
111
            command.prepend( "--" );
 
112
            command.prepend( userId );
 
113
            command.prepend( "-u" );
 
114
            command.prepend( KStandardDirs::findExe("kdesu") );
 
115
        }
 
116
    }
 
117
    if ( !clientMachine.isEmpty() && clientMachine != "localhost" ) {
 
118
        command.prepend( clientMachine );
 
119
        command.prepend( xonCommand ); // "xon" by default
 
120
    }
 
121
 
 
122
// TODO this function actually should not use KProcess at all and use klauncher (kdeinit) instead.
 
123
// Klauncher should also have support for tracking whether the launched process is still alive
 
124
// or not, so this should be redone. For now, use KProcess for wm's, as they need to be tracked,
 
125
// klauncher for the rest where ksmserver doesn't care.
 
126
    if( wm ) {
 
127
        KProcess* process = new KProcess( this );
 
128
        *process << command;
 
129
        // make it auto-delete
 
130
        connect( process, SIGNAL( error( QProcess::ProcessError )), process, SLOT( deleteLater()));
 
131
        connect( process, SIGNAL( finished( int, QProcess::ExitStatus )), process, SLOT( deleteLater()));
 
132
        process->start();
 
133
        return process;
 
134
    } else {
 
135
        int n = command.count();
 
136
        org::kde::KLauncher klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
 
137
        QString app = command[0];
 
138
        QStringList argList;
 
139
        for ( int i=1; i < n; i++)
 
140
           argList.append( command[i]);
 
141
        klauncher.exec_blind(app, argList );
 
142
        return NULL;
 
143
    }
 
144
}
 
145
 
 
146
/*! Utility function to execute a command on the local machine. Used
 
147
 * to discard session data
 
148
 */
 
149
void KSMServer::executeCommand( const QStringList& command )
 
150
{
 
151
    if ( command.isEmpty() )
 
152
        return;
 
153
 
 
154
    KProcess::execute( command );
 
155
}
 
156
 
 
157
IceAuthDataEntry *authDataEntries = 0;
 
158
 
 
159
static KTemporaryFile *remTempFile = 0;
 
160
 
 
161
static IceListenObj *listenObjs = 0;
 
162
int numTransports = 0;
 
163
static bool only_local = 0;
 
164
 
 
165
static Bool HostBasedAuthProc ( char* /*hostname*/)
 
166
{
 
167
    if (only_local)
 
168
        return true;
 
169
    else
 
170
        return false;
 
171
}
 
172
 
 
173
 
 
174
Status KSMRegisterClientProc (
 
175
    SmsConn             /* smsConn */,
 
176
    SmPointer           managerData,
 
177
    char *              previousId
 
178
)
 
179
{
 
180
    KSMClient* client = (KSMClient*) managerData;
 
181
    client->registerClient( previousId );
 
182
    return 1;
 
183
}
 
184
 
 
185
void KSMInteractRequestProc (
 
186
    SmsConn             /* smsConn */,
 
187
    SmPointer           managerData,
 
188
    int                 dialogType
 
189
)
 
190
{
 
191
    the_server->interactRequest( (KSMClient*) managerData, dialogType );
 
192
}
 
193
 
 
194
void KSMInteractDoneProc (
 
195
    SmsConn             /* smsConn */,
 
196
    SmPointer           managerData,
 
197
    Bool                        cancelShutdown
 
198
)
 
199
{
 
200
    the_server->interactDone( (KSMClient*) managerData, cancelShutdown );
 
201
}
 
202
 
 
203
void KSMSaveYourselfRequestProc (
 
204
    SmsConn             smsConn ,
 
205
    SmPointer           /* managerData */,
 
206
    int                 saveType,
 
207
    Bool                shutdown,
 
208
    int                 interactStyle,
 
209
    Bool                fast,
 
210
    Bool                global
 
211
)
 
212
{
 
213
    if ( shutdown ) {
 
214
        the_server->shutdown( fast ?
 
215
                              KWorkSpace::ShutdownConfirmNo :
 
216
                              KWorkSpace::ShutdownConfirmDefault,
 
217
                              KWorkSpace::ShutdownTypeDefault,
 
218
                              KWorkSpace::ShutdownModeDefault );
 
219
    } else if ( !global ) {
 
220
        SmsSaveYourself( smsConn, saveType, false, interactStyle, fast );
 
221
        SmsSaveComplete( smsConn );
 
222
    }
 
223
    // else checkpoint only, ksmserver does not yet support this
 
224
    // mode. Will come for KDE 3.1
 
225
}
 
226
 
 
227
void KSMSaveYourselfPhase2RequestProc (
 
228
    SmsConn             /* smsConn */,
 
229
    SmPointer           managerData
 
230
)
 
231
{
 
232
    the_server->phase2Request( (KSMClient*) managerData );
 
233
}
 
234
 
 
235
void KSMSaveYourselfDoneProc (
 
236
    SmsConn             /* smsConn */,
 
237
    SmPointer           managerData,
 
238
    Bool                success
 
239
)
 
240
{
 
241
    the_server->saveYourselfDone( (KSMClient*) managerData, success );
 
242
}
 
243
 
 
244
void KSMCloseConnectionProc (
 
245
    SmsConn             smsConn,
 
246
    SmPointer           managerData,
 
247
    int                 count,
 
248
    char **             reasonMsgs
 
249
)
 
250
{
 
251
    the_server->deleteClient( ( KSMClient* ) managerData );
 
252
    if ( count )
 
253
        SmFreeReasons( count, reasonMsgs );
 
254
    IceConn iceConn = SmsGetIceConnection( smsConn );
 
255
    SmsCleanUp( smsConn );
 
256
    IceSetShutdownNegotiation (iceConn, False);
 
257
    IceCloseConnection( iceConn );
 
258
}
 
259
 
 
260
void KSMSetPropertiesProc (
 
261
    SmsConn             /* smsConn */,
 
262
    SmPointer           managerData,
 
263
    int                 numProps,
 
264
    SmProp **           props
 
265
)
 
266
{
 
267
    KSMClient* client = ( KSMClient* ) managerData;
 
268
    for ( int i = 0; i < numProps; i++ ) {
 
269
        SmProp *p = client->property( props[i]->name );
 
270
        if ( p ) {
 
271
            client->properties.removeAll( p );
 
272
            SmFreeProperty( p );
 
273
        }
 
274
        client->properties.append( props[i] );
 
275
        if ( !qstrcmp( props[i]->name, SmProgram ) )
 
276
            the_server->clientSetProgram( client );
 
277
    }
 
278
 
 
279
    if ( numProps )
 
280
        free( props );
 
281
 
 
282
}
 
283
 
 
284
void KSMDeletePropertiesProc (
 
285
    SmsConn             /* smsConn */,
 
286
    SmPointer           managerData,
 
287
    int                 numProps,
 
288
    char **             propNames
 
289
)
 
290
{
 
291
    KSMClient* client = ( KSMClient* ) managerData;
 
292
    for ( int i = 0; i < numProps; i++ ) {
 
293
        SmProp *p = client->property( propNames[i] );
 
294
        if ( p ) {
 
295
            client->properties.removeAll( p );
 
296
            SmFreeProperty( p );
 
297
        }
 
298
    }
 
299
}
 
300
 
 
301
void KSMGetPropertiesProc (
 
302
    SmsConn             smsConn,
 
303
    SmPointer           managerData
 
304
)
 
305
{
 
306
    KSMClient* client = ( KSMClient* ) managerData;
 
307
    SmProp** props = new SmProp*[client->properties.count()];
 
308
    int i = 0;
 
309
    foreach( SmProp *prop, client->properties )
 
310
        props[i++] = prop;
 
311
 
 
312
    SmsReturnProperties( smsConn, i, props );
 
313
    delete [] props;
 
314
}
 
315
 
 
316
 
 
317
class KSMListener : public QSocketNotifier
 
318
{
 
319
public:
 
320
    KSMListener( IceListenObj obj )
 
321
        : QSocketNotifier( IceGetListenConnectionNumber( obj ),
 
322
                           QSocketNotifier::Read )
 
323
{
 
324
    listenObj = obj;
 
325
}
 
326
 
 
327
    IceListenObj listenObj;
 
328
};
 
329
 
 
330
class KSMConnection : public QSocketNotifier
 
331
{
 
332
 public:
 
333
  KSMConnection( IceConn conn )
 
334
    : QSocketNotifier( IceConnectionNumber( conn ),
 
335
                       QSocketNotifier::Read )
 
336
    {
 
337
        iceConn = conn;
 
338
    }
 
339
 
 
340
    IceConn iceConn;
 
341
};
 
342
 
 
343
 
 
344
/* for printing hex digits */
 
345
static void fprintfhex (FILE *fp, unsigned int len, char *cp)
 
346
{
 
347
    static const char hexchars[] = "0123456789abcdef";
 
348
 
 
349
    for (; len > 0; len--, cp++) {
 
350
        unsigned char s = *cp;
 
351
        putc(hexchars[s >> 4], fp);
 
352
        putc(hexchars[s & 0x0f], fp);
 
353
    }
 
354
}
 
355
 
 
356
/*
 
357
 * We use temporary files which contain commands to add/remove entries from
 
358
 * the .ICEauthority file.
 
359
 */
 
360
static void write_iceauth (FILE *addfp, FILE *removefp, IceAuthDataEntry *entry)
 
361
{
 
362
    fprintf (addfp,
 
363
             "add %s \"\" %s %s ",
 
364
             entry->protocol_name,
 
365
             entry->network_id,
 
366
             entry->auth_name);
 
367
    fprintfhex (addfp, entry->auth_data_length, entry->auth_data);
 
368
    fprintf (addfp, "\n");
 
369
 
 
370
    fprintf (removefp,
 
371
             "remove protoname=%s protodata=\"\" netid=%s authname=%s\n",
 
372
             entry->protocol_name,
 
373
             entry->network_id,
 
374
             entry->auth_name);
 
375
}
 
376
 
 
377
 
 
378
#define MAGIC_COOKIE_LEN 16
 
379
 
 
380
Status SetAuthentication_local (int count, IceListenObj *listenObjs)
 
381
{
 
382
    int i;
 
383
    for (i = 0; i < count; i ++) {
 
384
        char *prot = IceGetListenConnectionString(listenObjs[i]);
 
385
        if (!prot) continue;
 
386
        char *host = strchr(prot, '/');
 
387
        char *sock = 0;
 
388
        if (host) {
 
389
            *host=0;
 
390
            host++;
 
391
            sock = strchr(host, ':');
 
392
            if (sock) {
 
393
                *sock = 0;
 
394
                sock++;
 
395
            }
 
396
        }
 
397
        kDebug( 1218 ) << "KSMServer: SetAProc_loc: conn " << (unsigned)i << ", prot=" << prot << ", file=" << sock;
 
398
        if (sock && !strcmp(prot, "local")) {
 
399
            chmod(sock, 0700);
 
400
        }
 
401
        IceSetHostBasedAuthProc (listenObjs[i], HostBasedAuthProc);
 
402
        free(prot);
 
403
    }
 
404
    return 1;
 
405
}
 
406
 
 
407
Status SetAuthentication (int count, IceListenObj *listenObjs,
 
408
                          IceAuthDataEntry **authDataEntries)
 
409
{
 
410
    KTemporaryFile addTempFile;
 
411
    remTempFile = new KTemporaryFile;
 
412
 
 
413
    if (!addTempFile.open() || !remTempFile->open())
 
414
        return 0;
 
415
 
 
416
    if ((*authDataEntries = (IceAuthDataEntry *) malloc (
 
417
                         count * 2 * sizeof (IceAuthDataEntry))) == NULL)
 
418
        return 0;
 
419
 
 
420
    FILE *addAuthFile = fopen(QFile::encodeName(addTempFile.fileName()), "r+");
 
421
    FILE *remAuthFile = fopen(QFile::encodeName(remTempFile->fileName()), "r+");
 
422
 
 
423
    for (int i = 0; i < numTransports * 2; i += 2) {
 
424
        (*authDataEntries)[i].network_id =
 
425
            IceGetListenConnectionString (listenObjs[i/2]);
 
426
        (*authDataEntries)[i].protocol_name = (char *) "ICE";
 
427
        (*authDataEntries)[i].auth_name = (char *) "MIT-MAGIC-COOKIE-1";
 
428
 
 
429
        (*authDataEntries)[i].auth_data =
 
430
            IceGenerateMagicCookie (MAGIC_COOKIE_LEN);
 
431
        (*authDataEntries)[i].auth_data_length = MAGIC_COOKIE_LEN;
 
432
 
 
433
        (*authDataEntries)[i+1].network_id =
 
434
            IceGetListenConnectionString (listenObjs[i/2]);
 
435
        (*authDataEntries)[i+1].protocol_name = (char *) "XSMP";
 
436
        (*authDataEntries)[i+1].auth_name = (char *) "MIT-MAGIC-COOKIE-1";
 
437
 
 
438
        (*authDataEntries)[i+1].auth_data =
 
439
            IceGenerateMagicCookie (MAGIC_COOKIE_LEN);
 
440
        (*authDataEntries)[i+1].auth_data_length = MAGIC_COOKIE_LEN;
 
441
 
 
442
        write_iceauth (addAuthFile, remAuthFile, &(*authDataEntries)[i]);
 
443
        write_iceauth (addAuthFile, remAuthFile, &(*authDataEntries)[i+1]);
 
444
 
 
445
        IceSetPaAuthData (2, &(*authDataEntries)[i]);
 
446
 
 
447
        IceSetHostBasedAuthProc (listenObjs[i/2], HostBasedAuthProc);
 
448
    }
 
449
    fclose(addAuthFile);
 
450
    fclose(remAuthFile);
 
451
 
 
452
    QString iceAuth = KGlobal::dirs()->findExe("iceauth");
 
453
    if (iceAuth.isEmpty())
 
454
    {
 
455
        qWarning("KSMServer: could not find iceauth");
 
456
        return 0;
 
457
    }
 
458
 
 
459
    KProcess p;
 
460
    p << iceAuth << "source" << addTempFile.fileName();
 
461
    p.execute();
 
462
 
 
463
    return (1);
 
464
}
 
465
 
 
466
/*
 
467
 * Free up authentication data.
 
468
 */
 
469
void FreeAuthenticationData(int count, IceAuthDataEntry *authDataEntries)
 
470
{
 
471
    /* Each transport has entries for ICE and XSMP */
 
472
    if (only_local)
 
473
        return;
 
474
 
 
475
    for (int i = 0; i < count * 2; i++) {
 
476
        free (authDataEntries[i].network_id);
 
477
        free (authDataEntries[i].auth_data);
 
478
    }
 
479
 
 
480
    free (authDataEntries);
 
481
 
 
482
    QString iceAuth = KGlobal::dirs()->findExe("iceauth");
 
483
    if (iceAuth.isEmpty())
 
484
    {
 
485
        qWarning("KSMServer: could not find iceauth");
 
486
        return;
 
487
    }
 
488
 
 
489
    if (remTempFile)
 
490
    {
 
491
        KProcess p;
 
492
        p << iceAuth << "source" << remTempFile->fileName();
 
493
        p.execute();
 
494
    }
 
495
 
 
496
    delete remTempFile;
 
497
    remTempFile = 0;
 
498
}
 
499
 
 
500
static int Xio_ErrorHandler( Display * )
 
501
{
 
502
    qWarning("ksmserver: Fatal IO error: client killed");
 
503
 
 
504
    // Don't do anything that might require the X connection
 
505
    if (the_server)
 
506
    {
 
507
       KSMServer *server = the_server;
 
508
       the_server = 0;
 
509
       server->cleanUp();
 
510
       // Don't delete server!!
 
511
    }
 
512
 
 
513
    exit(0); // Don't report error, it's not our fault.
 
514
    return 0; // Bogus return value, notreached
 
515
}
 
516
 
 
517
void KSMServer::setupXIOErrorHandler()
 
518
{
 
519
    XSetIOErrorHandler(Xio_ErrorHandler);
 
520
}
 
521
 
 
522
static void sighandler(int sig)
 
523
{
 
524
    if (sig == SIGHUP) {
 
525
        signal(SIGHUP, sighandler);
 
526
        return;
 
527
    }
 
528
 
 
529
    if (the_server)
 
530
    {
 
531
       KSMServer *server = the_server;
 
532
       the_server = 0;
 
533
       server->cleanUp();
 
534
       delete server;
 
535
    }
 
536
 
 
537
    if (kapp)
 
538
        kapp->quit();
 
539
    //::exit(0);
 
540
}
 
541
 
 
542
 
 
543
void KSMWatchProc ( IceConn iceConn, IcePointer client_data, Bool opening, IcePointer* watch_data)
 
544
{
 
545
    KSMServer* ds = ( KSMServer*) client_data;
 
546
 
 
547
    if (opening) {
 
548
        *watch_data = (IcePointer) ds->watchConnection( iceConn );
 
549
    }
 
550
    else  {
 
551
        ds->removeConnection( (KSMConnection*) *watch_data );
 
552
    }
 
553
}
 
554
 
 
555
static Status KSMNewClientProc ( SmsConn conn, SmPointer manager_data,
 
556
                                 unsigned long* mask_ret, SmsCallbacks* cb, char** failure_reason_ret)
 
557
{
 
558
    *failure_reason_ret = 0;
 
559
 
 
560
    void* client =  ((KSMServer*) manager_data )->newClient( conn );
 
561
 
 
562
    cb->register_client.callback = KSMRegisterClientProc;
 
563
    cb->register_client.manager_data = client;
 
564
    cb->interact_request.callback = KSMInteractRequestProc;
 
565
    cb->interact_request.manager_data = client;
 
566
    cb->interact_done.callback = KSMInteractDoneProc;
 
567
    cb->interact_done.manager_data = client;
 
568
    cb->save_yourself_request.callback = KSMSaveYourselfRequestProc;
 
569
    cb->save_yourself_request.manager_data = client;
 
570
    cb->save_yourself_phase2_request.callback = KSMSaveYourselfPhase2RequestProc;
 
571
    cb->save_yourself_phase2_request.manager_data = client;
 
572
    cb->save_yourself_done.callback = KSMSaveYourselfDoneProc;
 
573
    cb->save_yourself_done.manager_data = client;
 
574
    cb->close_connection.callback = KSMCloseConnectionProc;
 
575
    cb->close_connection.manager_data = client;
 
576
    cb->set_properties.callback = KSMSetPropertiesProc;
 
577
    cb->set_properties.manager_data = client;
 
578
    cb->delete_properties.callback = KSMDeletePropertiesProc;
 
579
    cb->delete_properties.manager_data = client;
 
580
    cb->get_properties.callback = KSMGetPropertiesProc;
 
581
    cb->get_properties.manager_data = client;
 
582
 
 
583
    *mask_ret = SmsRegisterClientProcMask |
 
584
                SmsInteractRequestProcMask |
 
585
                SmsInteractDoneProcMask |
 
586
                SmsSaveYourselfRequestProcMask |
 
587
                SmsSaveYourselfP2RequestProcMask |
 
588
                SmsSaveYourselfDoneProcMask |
 
589
                SmsCloseConnectionProcMask |
 
590
                SmsSetPropertiesProcMask |
 
591
                SmsDeletePropertiesProcMask |
 
592
                SmsGetPropertiesProcMask;
 
593
    return 1;
 
594
}
 
595
 
 
596
 
 
597
#ifdef HAVE__ICETRANSNOLISTEN
 
598
extern "C" int _IceTransNoListen(const char * protocol);
 
599
#endif
 
600
 
 
601
KSMServer::KSMServer( const QString& windowManager, bool _only_local )
 
602
  : wmProcess( NULL )
 
603
  , sessionGroup( "" )
 
604
  , logoutEffectWidget( NULL )
 
605
{
 
606
    new KSMServerInterfaceAdaptor( this );
 
607
    QDBusConnection::sessionBus().registerObject("/KSMServer", this);
 
608
    klauncherSignals = new OrgKdeKLauncherInterface(QLatin1String("org.kde.klauncher"),
 
609
            QLatin1String("/KLauncher"), QDBusConnection::sessionBus());
 
610
    kcminitSignals = NULL;
 
611
    the_server = this;
 
612
    clean = false;
 
613
 
 
614
    shutdownType = KWorkSpace::ShutdownTypeNone;
 
615
 
 
616
    state = Idle;
 
617
    dialogActive = false;
 
618
    saveSession = false;
 
619
    wmPhase1WaitingCount = 0;
 
620
    KConfigGroup config(KGlobal::config(), "General");
 
621
    clientInteracting = 0;
 
622
    xonCommand = config.readEntry( "xonCommand", "xon" );
 
623
 
 
624
    KGlobal::dirs()->addResourceType( "windowmanagers", "data", "ksmserver/windowmanagers" );
 
625
    selectWm( windowManager );
 
626
 
 
627
    connect( &startupSuspendTimeoutTimer, SIGNAL( timeout()), SLOT( startupSuspendTimeout()));
 
628
    connect( &pendingShutdown, SIGNAL( timeout()), SLOT( pendingShutdownTimeout()));
 
629
 
 
630
    only_local = _only_local;
 
631
#ifdef HAVE__ICETRANSNOLISTEN
 
632
    if (only_local)
 
633
        _IceTransNoListen("tcp");
 
634
#else
 
635
    only_local = false;
 
636
#endif
 
637
 
 
638
    char        errormsg[256];
 
639
    if (!SmsInitialize ( (char*) KSMVendorString, (char*) KSMReleaseString,
 
640
                         KSMNewClientProc,
 
641
                         (SmPointer) this,
 
642
                         HostBasedAuthProc, 256, errormsg ) ) {
 
643
 
 
644
        qWarning("KSMServer: could not register XSM protocol");
 
645
    }
 
646
 
 
647
    if (!IceListenForConnections (&numTransports, &listenObjs,
 
648
                                  256, errormsg))
 
649
    {
 
650
        qWarning("KSMServer: Error listening for connections: %s", errormsg);
 
651
        qWarning("KSMServer: Aborting.");
 
652
        exit(1);
 
653
    }
 
654
 
 
655
    {
 
656
        // publish available transports.
 
657
        QByteArray fName = QFile::encodeName(KStandardDirs::locateLocal("socket", "KSMserver"));
 
658
        QString display = ::getenv("DISPLAY");
 
659
        // strip the screen number from the display
 
660
        display.replace(QRegExp("\\.[0-9]+$"), "");
 
661
        int i;
 
662
        while( (i = display.indexOf(':')) >= 0)
 
663
           display[i] = '_';
 
664
        while( (i = display.indexOf('/')) >= 0)
 
665
           display[i] = '_';
 
666
 
 
667
        fName += '_'+display.toLocal8Bit();
 
668
        FILE *f;
 
669
        f = ::fopen(fName.data(), "w+");
 
670
        if (!f)
 
671
        {
 
672
            qWarning("KSMServer: cannot open %s: %s", fName.data(), strerror(errno));
 
673
            qWarning("KSMServer: Aborting.");
 
674
            exit(1);
 
675
        }
 
676
        char* session_manager = IceComposeNetworkIdList(numTransports, listenObjs);
 
677
        fprintf(f, "%s\n%i\n", session_manager, getpid());
 
678
        fclose(f);
 
679
        setenv( "SESSION_MANAGER", session_manager, true  );
 
680
 
 
681
       // Pass env. var to kdeinit.
 
682
       org::kde::KLauncher klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
 
683
       klauncher.setLaunchEnv( "SESSION_MANAGER", (const char*) session_manager );
 
684
 
 
685
        free(session_manager);
 
686
    }
 
687
 
 
688
    if (only_local) {
 
689
        if (!SetAuthentication_local(numTransports, listenObjs))
 
690
            qFatal("KSMSERVER: authentication setup failed.");
 
691
    } else {
 
692
        if (!SetAuthentication(numTransports, listenObjs, &authDataEntries))
 
693
            qFatal("KSMSERVER: authentication setup failed.");
 
694
    }
 
695
 
 
696
    IceAddConnectionWatch (KSMWatchProc, (IcePointer) this);
 
697
 
 
698
    KSMListener* con;
 
699
    for ( int i = 0; i < numTransports; i++) {
 
700
        fcntl( IceGetListenConnectionNumber( listenObjs[i] ), F_SETFD, FD_CLOEXEC );
 
701
        con = new KSMListener( listenObjs[i] );
 
702
        listener.append( con );
 
703
        connect( con, SIGNAL( activated(int) ), this, SLOT( newConnection(int) ) );
 
704
    }
 
705
 
 
706
    signal(SIGHUP, sighandler);
 
707
    signal(SIGTERM, sighandler);
 
708
    signal(SIGINT, sighandler);
 
709
    signal(SIGPIPE, SIG_IGN);
 
710
 
 
711
    connect( &protectionTimer, SIGNAL( timeout() ), this, SLOT( protectionTimeout() ) );
 
712
    connect( &restoreTimer, SIGNAL( timeout() ), this, SLOT( tryRestoreNext() ) );
 
713
    connect( qApp, SIGNAL( aboutToQuit() ), this, SLOT( cleanUp() ) );
 
714
}
 
715
 
 
716
KSMServer::~KSMServer()
 
717
{
 
718
    qDeleteAll( listener );
 
719
    the_server = 0;
 
720
    cleanUp();
 
721
}
 
722
 
 
723
void KSMServer::cleanUp()
 
724
{
 
725
    if (clean) return;
 
726
    clean = true;
 
727
    IceFreeListenObjs (numTransports, listenObjs);
 
728
 
 
729
    QByteArray fName = QFile::encodeName(KStandardDirs::locateLocal("socket", "KSMserver"));
 
730
    QString display  = QString::fromLocal8Bit(::getenv("DISPLAY"));
 
731
    // strip the screen number from the display
 
732
    display.replace(QRegExp("\\.[0-9]+$"), "");
 
733
    int i;
 
734
    while( (i = display.indexOf(':')) >= 0)
 
735
         display[i] = '_';
 
736
    while( (i = display.indexOf('/')) >= 0)
 
737
         display[i] = '_';
 
738
 
 
739
    fName += '_'+display.toLocal8Bit();
 
740
    ::unlink(fName.data());
 
741
 
 
742
    FreeAuthenticationData(numTransports, authDataEntries);
 
743
    signal(SIGTERM, SIG_DFL);
 
744
    signal(SIGINT, SIG_DFL);
 
745
 
 
746
    KDisplayManager().shutdown( shutdownType, shutdownMode, bootOption );
 
747
}
 
748
 
 
749
 
 
750
 
 
751
void* KSMServer::watchConnection( IceConn iceConn )
 
752
{
 
753
    KSMConnection* conn = new KSMConnection( iceConn );
 
754
    connect( conn, SIGNAL( activated(int) ), this, SLOT( processData(int) ) );
 
755
    return (void*) conn;
 
756
}
 
757
 
 
758
void KSMServer::removeConnection( KSMConnection* conn )
 
759
{
 
760
    delete conn;
 
761
}
 
762
 
 
763
 
 
764
/*!
 
765
  Called from our IceIoErrorHandler
 
766
 */
 
767
void KSMServer::ioError( IceConn /*iceConn*/  )
 
768
{
 
769
}
 
770
 
 
771
void KSMServer::processData( int /*socket*/ )
 
772
{
 
773
    IceConn iceConn = ((KSMConnection*)sender())->iceConn;
 
774
    IceProcessMessagesStatus status = IceProcessMessages( iceConn, 0, 0 );
 
775
    if ( status == IceProcessMessagesIOError ) {
 
776
        IceSetShutdownNegotiation( iceConn, False );
 
777
        QList<KSMClient*>::iterator it = clients.begin();
 
778
        QList<KSMClient*>::iterator const itEnd = clients.end();
 
779
        while ( ( it != itEnd ) && *it && ( SmsGetIceConnection( ( *it )->connection() ) != iceConn ) )
 
780
            ++it;
 
781
        if ( ( it != itEnd ) && *it ) {
 
782
            SmsConn smsConn = (*it)->connection();
 
783
            deleteClient( *it );
 
784
            SmsCleanUp( smsConn );
 
785
        }
 
786
        (void) IceCloseConnection( iceConn );
 
787
    }
 
788
}
 
789
 
 
790
KSMClient* KSMServer::newClient( SmsConn conn )
 
791
{
 
792
    KSMClient* client = new KSMClient( conn );
 
793
    clients.append( client );
 
794
    return client;
 
795
}
 
796
 
 
797
void KSMServer::deleteClient( KSMClient* client )
 
798
{
 
799
    if ( !clients.contains( client ) ) // paranoia
 
800
        return;
 
801
    clients.removeAll( client );
 
802
    clientsToKill.removeAll( client );
 
803
    clientsToSave.removeAll( client );
 
804
    if ( client == clientInteracting ) {
 
805
        clientInteracting = 0;
 
806
        handlePendingInteractions();
 
807
    }
 
808
    delete client;
 
809
    if ( state == Shutdown || state == Checkpoint || state == ClosingSubSession )
 
810
        completeShutdownOrCheckpoint();
 
811
    if ( state == Killing )
 
812
        completeKilling();
 
813
    else if ( state == KillingSubSession )
 
814
        completeKillingSubSession();
 
815
    if ( state == KillingWM )
 
816
        completeKillingWM();
 
817
}
 
818
 
 
819
void KSMServer::newConnection( int /*socket*/ )
 
820
{
 
821
    IceAcceptStatus status;
 
822
    IceConn iceConn = IceAcceptConnection( ((KSMListener*)sender())->listenObj, &status);
 
823
    if( iceConn == NULL )
 
824
        return;
 
825
    IceSetShutdownNegotiation( iceConn, False );
 
826
    IceConnectStatus cstatus;
 
827
    while ((cstatus = IceConnectionStatus (iceConn))==IceConnectPending) {
 
828
        (void) IceProcessMessages( iceConn, 0, 0 );
 
829
    }
 
830
 
 
831
    if (cstatus != IceConnectAccepted) {
 
832
        if (cstatus == IceConnectIOError)
 
833
            kDebug( 1218 ) << "IO error opening ICE Connection!";
 
834
        else
 
835
            kDebug( 1218 ) << "ICE Connection rejected!";
 
836
        (void )IceCloseConnection (iceConn);
 
837
        return;
 
838
    }
 
839
 
 
840
    // don't leak the fd
 
841
    fcntl( IceConnectionNumber(iceConn), F_SETFD, FD_CLOEXEC );
 
842
}
 
843
 
 
844
 
 
845
QString KSMServer::currentSession()
 
846
{
 
847
    if ( sessionGroup.startsWith( "Session: " ) )
 
848
        return sessionGroup.mid( 9 );
 
849
    return ""; // empty, not null, since used for KConfig::setGroup
 
850
}
 
851
 
 
852
void KSMServer::discardSession()
 
853
{
 
854
    KConfigGroup config(KGlobal::config(), sessionGroup );
 
855
    int count =  config.readEntry( "count", 0 );
 
856
        foreach ( KSMClient *c, clients ) {
 
857
        QStringList discardCommand = c->discardCommand();
 
858
        if ( discardCommand.isEmpty())
 
859
            continue;
 
860
        // check that non of the old clients used the exactly same
 
861
        // discardCommand before we execute it. This used to be the
 
862
        // case up to KDE and Qt < 3.1
 
863
        int i = 1;
 
864
        while ( i <= count &&
 
865
                config.readPathEntry( QString("discardCommand") + QString::number(i), QStringList() ) != discardCommand )
 
866
            i++;
 
867
        if ( i <= count )
 
868
            executeCommand( discardCommand );
 
869
    }
 
870
}
 
871
 
 
872
void KSMServer::storeSession()
 
873
{
 
874
    KSharedConfig::Ptr config = KGlobal::config();
 
875
    config->reparseConfiguration(); // config may have changed in the KControl module
 
876
    KConfigGroup generalGroup(config, "General");
 
877
    excludeApps = generalGroup.readEntry( "excludeApps" ).toLower().split( QRegExp( "[,:]" ), QString::SkipEmptyParts );
 
878
    KConfigGroup configSessionGroup(config, sessionGroup);
 
879
    int count =  configSessionGroup.readEntry( "count", 0 );
 
880
    for ( int i = 1; i <= count; i++ ) {
 
881
        QStringList discardCommand = configSessionGroup.readPathEntry( QLatin1String("discardCommand") + QString::number(i), QStringList() );
 
882
        if ( discardCommand.isEmpty())
 
883
            continue;
 
884
        // check that non of the new clients uses the exactly same
 
885
        // discardCommand before we execute it. This used to be the
 
886
        // case up to KDE and Qt < 3.1
 
887
        QList<KSMClient*>::iterator it = clients.begin();
 
888
        QList<KSMClient*>::iterator const itEnd = clients.end();
 
889
        while ( ( it != itEnd ) && *it && (discardCommand != ( *it )->discardCommand() ) )
 
890
            ++it;
 
891
        if ( ( it != itEnd ) && *it )
 
892
            continue;
 
893
        executeCommand( discardCommand );
 
894
    }
 
895
    config->deleteGroup( sessionGroup ); //### does not work with global config object...
 
896
        KConfigGroup cg( config, sessionGroup);
 
897
    count =  0;
 
898
 
 
899
    if (state != ClosingSubSession) {
 
900
        // put the wm first
 
901
        foreach ( KSMClient *c, clients )
 
902
            if ( c->program() == wm ) {
 
903
                clients.removeAll( c );
 
904
                clients.prepend( c );
 
905
                break;
 
906
            }
 
907
    }
 
908
 
 
909
    foreach ( KSMClient *c, clients ) {
 
910
        int restartHint = c->restartStyleHint();
 
911
        if (restartHint == SmRestartNever)
 
912
           continue;
 
913
        QString program = c->program();
 
914
        QStringList restartCommand = c->restartCommand();
 
915
        if (program.isEmpty() && restartCommand.isEmpty())
 
916
           continue;
 
917
        if (state == ClosingSubSession && ! clientsToSave.contains(c))
 
918
            continue;
 
919
        if (excludeApps.contains( program.toLower()))
 
920
            continue;
 
921
 
 
922
        count++;
 
923
        QString n = QString::number(count);
 
924
        cg.writeEntry( QString("program")+n, program );
 
925
        cg.writeEntry( QString("clientId")+n, c->clientId() );
 
926
        cg.writeEntry( QString("restartCommand")+n, restartCommand );
 
927
        cg.writePathEntry( QString("discardCommand")+n, c->discardCommand() );
 
928
        cg.writeEntry( QString("restartStyleHint")+n, restartHint );
 
929
        cg.writeEntry( QString("userId")+n, c->userId() );
 
930
        cg.writeEntry( QString("wasWm")+n, isWM( c ));
 
931
    }
 
932
    cg.writeEntry( "count", count );
 
933
 
 
934
    KConfigGroup cg2( config, "General");
 
935
    cg2.writeEntry( "screenCount", ScreenCount(QX11Info::display()));
 
936
 
 
937
    storeLegacySession(config.data());
 
938
    config->sync();
 
939
}
 
940
 
 
941
QStringList KSMServer::sessionList()
 
942
{
 
943
    QStringList sessions ( "default" );
 
944
    KSharedConfig::Ptr config = KGlobal::config();
 
945
    const QStringList groups = config->groupList();
 
946
    for ( QStringList::ConstIterator it = groups.constBegin(); it != groups.constEnd(); it++ )
 
947
        if ( (*it).startsWith( "Session: " ) )
 
948
            sessions << (*it).mid( 9 );
 
949
    return sessions;
 
950
}
 
951
 
 
952
bool KSMServer::isWM( const KSMClient* client ) const
 
953
{
 
954
    return isWM( client->program());
 
955
}
 
956
 
 
957
bool KSMServer::isWM( const QString& command ) const
 
958
{
 
959
    return command == wm;
 
960
}
 
961
 
 
962
bool KSMServer::defaultSession() const
 
963
{
 
964
    return sessionGroup.isEmpty();
 
965
}
 
966
 
 
967
// selection logic:
 
968
// - $KDEWM is set - use that
 
969
// - a wm is selected using the kcm - use that
 
970
// - if that fails, just use KWin
 
971
void KSMServer::selectWm( const QString& kdewm )
 
972
{
 
973
    wm = "kwin"; // defaults
 
974
    wmCommands = ( QStringList() << "kwin" ); 
 
975
    if( qstrcmp( getenv( "KDE_FAILSAFE" ), "1" ) == 0 )
 
976
        return; // failsafe, force kwin
 
977
    if( !kdewm.isEmpty())
 
978
    {
 
979
        wmCommands = ( QStringList() << kdewm );
 
980
        wm = kdewm;
 
981
        return;
 
982
    }
 
983
    KConfigGroup config(KGlobal::config(), "General");
 
984
    QString cfgwm = config.readEntry( "windowManager", "kwin" );
 
985
    KDesktopFile file( "windowmanagers", cfgwm + ".desktop" );
 
986
    if( file.noDisplay())
 
987
        return;
 
988
    if( !file.tryExec())
 
989
        return;
 
990
    QString testexec = file.desktopGroup().readEntry( "X-KDE-WindowManagerTestExec" );
 
991
    if( !testexec.isEmpty())
 
992
    {
 
993
        KProcess proc;
 
994
        proc.setShellCommand( testexec );
 
995
        if( proc.execute() != 0 )
 
996
            return;
 
997
    }
 
998
    QStringList cfgWmCommands = KShell::splitArgs( file.desktopGroup().readEntry( "Exec" ));
 
999
    if( cfgWmCommands.isEmpty())
 
1000
        return;
 
1001
    QString smname = file.desktopGroup().readEntry( "X-KDE-WindowManagerId" );
 
1002
    // ok
 
1003
    wm = smname.isEmpty() ? cfgwm : smname;
 
1004
    wmCommands = cfgWmCommands;
 
1005
}
 
1006
 
 
1007
void KSMServer::wmChanged()
 
1008
{
 
1009
    KGlobal::config()->reparseConfiguration();
 
1010
    selectWm( "" );
 
1011
}
 
1012
 
 
1013
void KSMServer::setupShortcuts()
 
1014
{
 
1015
    if (KAuthorized::authorize("logout")) {
 
1016
        KActionCollection* actionCollection = new KActionCollection(this);
 
1017
        KAction* a;
 
1018
        a = actionCollection->addAction("Log Out");
 
1019
        a->setText(i18n("Log Out"));
 
1020
        a->setGlobalShortcut(KShortcut(Qt::ALT+Qt::CTRL+Qt::Key_Delete));
 
1021
        connect(a, SIGNAL(triggered(bool)), SLOT(defaultLogout()));
 
1022
 
 
1023
        a = actionCollection->addAction("Log Out Without Confirmation");
 
1024
        a->setText(i18n("Log Out Without Confirmation"));
 
1025
        a->setGlobalShortcut(KShortcut(Qt::ALT+Qt::CTRL+Qt::SHIFT+Qt::Key_Delete));
 
1026
        connect(a, SIGNAL(triggered(bool)), SLOT(logoutWithoutConfirmation()));
 
1027
 
 
1028
        a = actionCollection->addAction("Halt Without Confirmation");
 
1029
        a->setText(i18n("Halt Without Confirmation"));
 
1030
        a->setGlobalShortcut(KShortcut(Qt::ALT+Qt::CTRL+Qt::SHIFT+Qt::Key_PageDown));
 
1031
        connect(a, SIGNAL(triggered(bool)), SLOT(haltWithoutConfirmation()));
 
1032
 
 
1033
        a = actionCollection->addAction("Reboot Without Confirmation");
 
1034
        a->setText(i18n("Reboot Without Confirmation"));
 
1035
        a->setGlobalShortcut(KShortcut(Qt::ALT+Qt::CTRL+Qt::SHIFT+Qt::Key_PageUp));
 
1036
        connect(a, SIGNAL(triggered(bool)), SLOT(rebootWithoutConfirmation()));
 
1037
    }
 
1038
}
 
1039
 
 
1040
void KSMServer::defaultLogout()
 
1041
{
 
1042
    shutdown(KWorkSpace::ShutdownConfirmYes, KWorkSpace::ShutdownTypeDefault, KWorkSpace::ShutdownModeDefault);
 
1043
}
 
1044
 
 
1045
void KSMServer::logoutWithoutConfirmation()
 
1046
{
 
1047
    shutdown(KWorkSpace::ShutdownConfirmNo, KWorkSpace::ShutdownTypeNone, KWorkSpace::ShutdownModeDefault);
 
1048
}
 
1049
 
 
1050
void KSMServer::haltWithoutConfirmation()
 
1051
{
 
1052
    shutdown(KWorkSpace::ShutdownConfirmNo, KWorkSpace::ShutdownTypeHalt, KWorkSpace::ShutdownModeDefault);
 
1053
}
 
1054
 
 
1055
void KSMServer::rebootWithoutConfirmation()
 
1056
{
 
1057
    shutdown(KWorkSpace::ShutdownConfirmNo, KWorkSpace::ShutdownTypeReboot, KWorkSpace::ShutdownModeDefault);
 
1058
}