not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / libs / kworkspace / kdisplaymanager.cpp
blob2ce45efecb787bc725d563cbb252c6887c018a9f
1 /*
2 Copyright (C) 2004 Oswald Buddenhagen <ossi@kde.org>
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the Lesser GNU General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
14 You should have received a copy of the Lesser GNU General Public License
15 along with this program; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #include "kdisplaymanager.h"
22 #ifdef Q_WS_X11
24 #include <kapplication.h>
25 #include <klocale.h>
26 #include <QtDBus/QtDBus>
28 #include <QRegExp>
30 #include <X11/Xauth.h>
31 #include <X11/Xlib.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/un.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <stdio.h>
42 static enum { Dunno, NoDM, NewKDM, OldKDM, GDM } DMType = Dunno;
43 static const char *ctl, *dpy;
45 class KDisplayManager::Private
47 public:
48 Private() : fd(-1) {}
49 ~Private() {
50 if (fd >= 0)
51 close( fd );
54 int fd;
57 KDisplayManager::KDisplayManager() : d(new Private)
59 const char *ptr;
60 struct sockaddr_un sa;
62 if (DMType == Dunno) {
63 if (!(dpy = ::getenv( "DISPLAY" )))
64 DMType = NoDM;
65 else if ((ctl = ::getenv( "DM_CONTROL" )))
66 DMType = NewKDM;
67 else if ((ctl = ::getenv( "XDM_MANAGED" )) && ctl[0] == '/')
68 DMType = OldKDM;
69 else if (::getenv( "GDMSESSION" ))
70 DMType = GDM;
71 else
72 DMType = NoDM;
74 switch (DMType) {
75 default:
76 return;
77 case NewKDM:
78 case GDM:
79 if ((d->fd = ::socket( PF_UNIX, SOCK_STREAM, 0 )) < 0)
80 return;
81 sa.sun_family = AF_UNIX;
82 if (DMType == GDM) {
83 strcpy( sa.sun_path, "/var/run/gdm_socket" );
84 if (::connect( d->fd, (struct sockaddr *)&sa, sizeof(sa) )) {
85 strcpy( sa.sun_path, "/tmp/.gdm_socket" );
86 if (::connect( d->fd, (struct sockaddr *)&sa, sizeof(sa) )) {
87 ::close( d->fd );
88 d->fd = -1;
89 break;
92 GDMAuthenticate();
93 } else {
94 if ((ptr = strchr( dpy, ':' )))
95 ptr = strchr( ptr, '.' );
96 snprintf( sa.sun_path, sizeof(sa.sun_path),
97 "%s/dmctl-%.*s/socket",
98 ctl, ptr ? int(ptr - dpy) : 512, dpy );
99 if (::connect( d->fd, (struct sockaddr *)&sa, sizeof(sa) )) {
100 ::close( d->fd );
101 d->fd = -1;
104 break;
105 case OldKDM:
107 QString tf( ctl );
108 tf.truncate( tf.indexOf( ',' ) );
109 d->fd = ::open( tf.toLatin1(), O_WRONLY );
111 break;
115 KDisplayManager::~KDisplayManager()
117 delete d;
120 bool
121 KDisplayManager::exec( const char *cmd )
123 QByteArray buf;
125 return exec( cmd, buf );
129 * Execute a KDM/GDM remote control command.
130 * @param cmd the command to execute. FIXME: undocumented yet.
131 * @param buf the result buffer.
132 * @return result:
133 * @li If true, the command was successfully executed.
134 * @p ret might contain addional results.
135 * @li If false and @p ret is empty, a communication error occurred
136 * (most probably KDM is not running).
137 * @li If false and @p ret is non-empty, it contains the error message
138 * from KDM.
140 bool
141 KDisplayManager::exec( const char *cmd, QByteArray &buf )
143 bool ret = false;
144 int tl;
145 int len = 0;
147 if (d->fd < 0)
148 goto busted;
150 tl = strlen( cmd );
151 if (::write( d->fd, cmd, tl ) != tl) {
152 bust:
153 ::close( d->fd );
154 d->fd = -1;
155 busted:
156 buf.resize( 0 );
157 return false;
159 if (DMType == OldKDM) {
160 buf.resize( 0 );
161 return true;
163 for (;;) {
164 if (buf.size() < 128)
165 buf.resize( 128 );
166 else if (buf.size() < len * 2)
167 buf.resize( len * 2 );
168 if ((tl = ::read( d->fd, buf.data() + len, buf.size() - len)) <= 0) {
169 if (tl < 0 && errno == EINTR)
170 continue;
171 goto bust;
173 len += tl;
174 if (buf[len - 1] == '\n') {
175 buf[len - 1] = 0;
176 if (len > 2 && (buf[0] == 'o' || buf[0] == 'O') &&
177 (buf[1] == 'k' || buf[1] == 'K') && buf[2] <= ' ')
178 ret = true;
179 break;
182 return ret;
185 bool
186 KDisplayManager::canShutdown()
188 if (DMType == OldKDM)
189 return strstr( ctl, ",maysd" ) != 0;
191 QByteArray re;
193 if (DMType == GDM)
194 return exec( "QUERY_LOGOUT_ACTION\n", re ) && re.indexOf( "HALT" ) >= 0;
196 return exec( "caps\n", re ) && re.indexOf( "\tshutdown" ) >= 0;
199 void
200 KDisplayManager::shutdown( KWorkSpace::ShutdownType shutdownType,
201 KWorkSpace::ShutdownMode shutdownMode, /* NOT Default */
202 const QString &bootOption )
204 if (shutdownType == KWorkSpace::ShutdownTypeNone || shutdownType == KWorkSpace::ShutdownTypeLogout)
205 return;
207 bool cap_ask;
208 if (DMType == NewKDM) {
209 QByteArray re;
210 cap_ask = exec( "caps\n", re ) && re.indexOf( "\tshutdown ask" ) >= 0;
211 } else {
212 if (!bootOption.isEmpty())
213 return;
214 cap_ask = false;
216 if (!cap_ask && shutdownMode == KWorkSpace::ShutdownModeInteractive)
217 shutdownMode = KWorkSpace::ShutdownModeForceNow;
219 QByteArray cmd;
220 if (DMType == GDM) {
221 cmd.append( shutdownMode == KWorkSpace::ShutdownModeForceNow ?
222 "SET_LOGOUT_ACTION " : "SET_SAFE_LOGOUT_ACTION " );
223 cmd.append( shutdownType == KWorkSpace::ShutdownTypeReboot ?
224 "REBOOT\n" : "HALT\n" );
225 } else {
226 cmd.append( "shutdown\t" );
227 cmd.append( shutdownType == KWorkSpace::ShutdownTypeReboot ?
228 "reboot\t" : "halt\t" );
229 if (!bootOption.isEmpty())
230 cmd.append( "=" ).append( bootOption.toLocal8Bit() ).append( "\t" );
231 cmd.append( shutdownMode == KWorkSpace::ShutdownModeInteractive ?
232 "ask\n" :
233 shutdownMode == KWorkSpace::ShutdownModeForceNow ?
234 "forcenow\n" :
235 shutdownMode == KWorkSpace::ShutdownModeTryNow ?
236 "trynow\n" : "schedule\n" );
238 exec( cmd.data() );
241 bool
242 KDisplayManager::bootOptions( QStringList &opts, int &defopt, int &current )
244 if (DMType != NewKDM)
245 return false;
247 QByteArray re;
248 if (!exec( "listbootoptions\n", re ))
249 return false;
251 opts = QString::fromLocal8Bit( re.data() ).split( '\t', QString::SkipEmptyParts );
252 if (opts.size() < 4)
253 return false;
255 bool ok;
256 defopt = opts[2].toInt( &ok );
257 if (!ok)
258 return false;
259 current = opts[3].toInt( &ok );
260 if (!ok)
261 return false;
263 opts = opts[1].split( ' ', QString::SkipEmptyParts );
264 for (QStringList::Iterator it = opts.begin(); it != opts.end(); ++it)
265 (*it).replace( "\\s", " " );
267 return true;
270 void
271 KDisplayManager::setLock( bool on )
273 if (DMType != GDM)
274 exec( on ? "lock\n" : "unlock\n" );
277 bool
278 KDisplayManager::isSwitchable()
280 if (DMType == OldKDM)
281 return dpy[0] == ':';
283 if (DMType == GDM)
284 return exec( "QUERY_VT\n" );
286 QByteArray re;
288 return exec( "caps\n", re ) && re.indexOf( "\tlocal" ) >= 0;
292 KDisplayManager::numReserve()
294 if (DMType == GDM)
295 return 1; /* Bleh */
297 if (DMType == OldKDM)
298 return strstr( ctl, ",rsvd" ) ? 1 : -1;
300 QByteArray re;
301 int p;
303 if (!(exec( "caps\n", re ) && (p = re.indexOf( "\treserve " )) >= 0))
304 return -1;
305 return atoi( re.data() + p + 9 );
308 void
309 KDisplayManager::startReserve()
311 if (DMType == GDM)
312 exec("FLEXI_XSERVER\n");
313 else
314 exec("reserve\n");
317 bool
318 KDisplayManager::localSessions( SessList &list )
320 if (DMType == OldKDM)
321 return false;
323 QByteArray re;
325 if (DMType == GDM) {
326 if (!exec( "CONSOLE_SERVERS\n", re ))
327 return false;
328 const QStringList sess = QString(re.data() +3).split( QChar(';'), QString::SkipEmptyParts);
329 for (QStringList::ConstIterator it = sess.constBegin(); it != sess.constEnd(); ++it) {
330 QStringList ts = (*it).split( QChar(',') );
331 SessEnt se;
332 se.display = ts[0];
333 se.user = ts[1];
334 se.vt = ts[2].toInt();
335 se.session = "<unknown>";
336 se.self = ts[0] == ::getenv( "DISPLAY" ); /* Bleh */
337 se.tty = false;
338 list.append( se );
340 } else {
341 if (!exec( "list\talllocal\n", re ))
342 return false;
343 const QStringList sess = QString(re.data() + 3).split(QChar('\t'), QString::SkipEmptyParts );
344 for (QStringList::ConstIterator it = sess.constBegin(); it != sess.constEnd(); ++it) {
345 QStringList ts = (*it).split( QChar(',') );
346 SessEnt se;
347 se.display = ts[0];
348 if (ts[1][0] == '@')
349 se.from = ts[1].mid( 1 ), se.vt = 0;
350 else
351 se.vt = ts[1].mid( 2 ).toInt();
352 se.user = ts[2];
353 se.session = ts[3];
354 se.self = (ts[4].indexOf( '*' ) >= 0);
355 se.tty = (ts[4].indexOf( 't' ) >= 0);
356 list.append( se );
359 return true;
362 void
363 KDisplayManager::sess2Str2( const SessEnt &se, QString &user, QString &loc )
365 if (se.tty) {
366 user = i18nc("user: ...", "%1: TTY login", se.user );
367 loc = se.vt ? QString("vt%1").arg( se.vt ) : se.display ;
368 } else {
369 user =
370 se.user.isEmpty() ?
371 se.session.isEmpty() ?
372 i18nc("... location (TTY or X display)", "Unused") :
373 se.session == "<remote>" ?
374 i18n("X login on remote host") :
375 i18nc("... host", "X login on %1", se.session ) :
376 se.session == "<unknown>" ?
377 se.user :
378 i18nc("user: session type", "%1: %2",
379 se.user, se.session );
380 loc =
381 se.vt ?
382 QString("%1, vt%2").arg( se.display ).arg( se.vt ) :
383 se.display;
387 QString
388 KDisplayManager::sess2Str( const SessEnt &se )
390 QString user, loc;
392 sess2Str2( se, user, loc );
393 return i18nc("session (location)", "%1 (%2)", user, loc );
396 bool
397 KDisplayManager::switchVT( int vt )
399 if (DMType == GDM)
400 return exec( QString("SET_VT %1\n").arg(vt).toLatin1() );
402 return exec( QString("activate\tvt%1\n").arg(vt).toLatin1() );
405 void
406 KDisplayManager::lockSwitchVT( int vt )
408 if (switchVT( vt ))
410 QDBusInterface screensaver("org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver");
411 screensaver.call( "Lock" );
415 void
416 KDisplayManager::GDMAuthenticate()
418 FILE *fp;
419 const char *dpy, *dnum, *dne;
420 int dnl;
421 Xauth *xau;
423 dpy = DisplayString( QX11Info::display() );
424 if (!dpy) {
425 dpy = ::getenv( "DISPLAY" );
426 if (!dpy)
427 return;
429 dnum = strchr( dpy, ':' ) + 1;
430 dne = strchr( dpy, '.' );
431 dnl = dne ? dne - dnum : strlen( dnum );
433 /* XXX should do locking */
434 if (!(fp = fopen( XauFileName(), "r" )))
435 return;
437 while ((xau = XauReadAuth( fp ))) {
438 if (xau->family == FamilyLocal &&
439 xau->number_length == dnl && !memcmp( xau->number, dnum, dnl ) &&
440 xau->data_length == 16 &&
441 xau->name_length == 18 && !memcmp( xau->name, "MIT-MAGIC-COOKIE-1", 18 ))
443 QString cmd( "AUTH_LOCAL " );
444 for (int i = 0; i < 16; i++)
445 cmd += QString::number( (uchar)xau->data[i], 16 ).rightJustified( 2, '0');
446 cmd += '\n';
447 if (exec( cmd.toLatin1() )) {
448 XauDisposeAuth( xau );
449 break;
452 XauDisposeAuth( xau );
455 fclose (fp);
458 #endif // Q_WS_X11