00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "kdisplaymanager.h"
00021
00022 #ifdef Q_WS_X11
00023
00024 #include <kapplication.h>
00025 #include <klocale.h>
00026 #include <QtDBus/QtDBus>
00027
00028 #include <QRegExp>
00029
00030 #include <X11/Xauth.h>
00031 #include <X11/Xlib.h>
00032
00033 #include <sys/types.h>
00034 #include <sys/socket.h>
00035 #include <sys/un.h>
00036 #include <unistd.h>
00037 #include <stdlib.h>
00038 #include <fcntl.h>
00039 #include <errno.h>
00040 #include <stdio.h>
00041
00042 static enum { Dunno, NoDM, NewKDM, OldKDM, GDM } DMType = Dunno;
00043 static const char *ctl, *dpy;
00044
00045 class KDisplayManager::Private
00046 {
00047 public:
00048 Private() : fd(-1) {}
00049 ~Private() {
00050 if (fd >= 0)
00051 close( fd );
00052 }
00053
00054 int fd;
00055 };
00056
00057 KDisplayManager::KDisplayManager() : d(new Private)
00058 {
00059 const char *ptr;
00060 struct sockaddr_un sa;
00061
00062 if (DMType == Dunno) {
00063 if (!(dpy = ::getenv( "DISPLAY" )))
00064 DMType = NoDM;
00065 else if ((ctl = ::getenv( "DM_CONTROL" )))
00066 DMType = NewKDM;
00067 else if ((ctl = ::getenv( "XDM_MANAGED" )) && ctl[0] == '/')
00068 DMType = OldKDM;
00069 else if (::getenv( "GDMSESSION" ))
00070 DMType = GDM;
00071 else
00072 DMType = NoDM;
00073 }
00074 switch (DMType) {
00075 default:
00076 return;
00077 case NewKDM:
00078 case GDM:
00079 if ((d->fd = ::socket( PF_UNIX, SOCK_STREAM, 0 )) < 0)
00080 return;
00081 sa.sun_family = AF_UNIX;
00082 if (DMType == GDM) {
00083 strcpy( sa.sun_path, "/var/run/gdm_socket" );
00084 if (::connect( d->fd, (struct sockaddr *)&sa, sizeof(sa) )) {
00085 strcpy( sa.sun_path, "/tmp/.gdm_socket" );
00086 if (::connect( d->fd, (struct sockaddr *)&sa, sizeof(sa) )) {
00087 ::close( d->fd );
00088 d->fd = -1;
00089 break;
00090 }
00091 }
00092 GDMAuthenticate();
00093 } else {
00094 if ((ptr = strchr( dpy, ':' )))
00095 ptr = strchr( ptr, '.' );
00096 snprintf( sa.sun_path, sizeof(sa.sun_path),
00097 "%s/dmctl-%.*s/socket",
00098 ctl, ptr ? int(ptr - dpy) : 512, dpy );
00099 if (::connect( d->fd, (struct sockaddr *)&sa, sizeof(sa) )) {
00100 ::close( d->fd );
00101 d->fd = -1;
00102 }
00103 }
00104 break;
00105 case OldKDM:
00106 {
00107 QString tf( ctl );
00108 tf.truncate( tf.indexOf( ',' ) );
00109 d->fd = ::open( tf.toLatin1(), O_WRONLY );
00110 }
00111 break;
00112 }
00113 }
00114
00115 KDisplayManager::~KDisplayManager()
00116 {
00117 delete d;
00118 }
00119
00120 bool
00121 KDisplayManager::exec( const char *cmd )
00122 {
00123 QByteArray buf;
00124
00125 return exec( cmd, buf );
00126 }
00127
00140 bool
00141 KDisplayManager::exec( const char *cmd, QByteArray &buf )
00142 {
00143 bool ret = false;
00144 int tl;
00145 int len = 0;
00146
00147 if (d->fd < 0)
00148 goto busted;
00149
00150 tl = strlen( cmd );
00151 if (::write( d->fd, cmd, tl ) != tl) {
00152 bust:
00153 ::close( d->fd );
00154 d->fd = -1;
00155 busted:
00156 buf.resize( 0 );
00157 return false;
00158 }
00159 if (DMType == OldKDM) {
00160 buf.resize( 0 );
00161 return true;
00162 }
00163 for (;;) {
00164 if (buf.size() < 128)
00165 buf.resize( 128 );
00166 else if (buf.size() < len * 2)
00167 buf.resize( len * 2 );
00168 if ((tl = ::read( d->fd, buf.data() + len, buf.size() - len)) <= 0) {
00169 if (tl < 0 && errno == EINTR)
00170 continue;
00171 goto bust;
00172 }
00173 len += tl;
00174 if (buf[len - 1] == '\n') {
00175 buf[len - 1] = 0;
00176 if (len > 2 && (buf[0] == 'o' || buf[0] == 'O') &&
00177 (buf[1] == 'k' || buf[1] == 'K') && buf[2] <= ' ')
00178 ret = true;
00179 break;
00180 }
00181 }
00182 return ret;
00183 }
00184
00185 bool
00186 KDisplayManager::canShutdown()
00187 {
00188 if (DMType == OldKDM)
00189 return strstr( ctl, ",maysd" ) != 0;
00190
00191 QByteArray re;
00192
00193 if (DMType == GDM)
00194 return exec( "QUERY_LOGOUT_ACTION\n", re ) && re.indexOf( "HALT" ) >= 0;
00195
00196 return exec( "caps\n", re ) && re.indexOf( "\tshutdown" ) >= 0;
00197 }
00198
00199 void
00200 KDisplayManager::shutdown( KWorkSpace::ShutdownType shutdownType,
00201 KWorkSpace::ShutdownMode shutdownMode,
00202 const QString &bootOption )
00203 {
00204 if (shutdownType == KWorkSpace::ShutdownTypeNone || shutdownType == KWorkSpace::ShutdownTypeLogout)
00205 return;
00206
00207 bool cap_ask;
00208 if (DMType == NewKDM) {
00209 QByteArray re;
00210 cap_ask = exec( "caps\n", re ) && re.indexOf( "\tshutdown ask" ) >= 0;
00211 } else {
00212 if (!bootOption.isEmpty())
00213 return;
00214 cap_ask = false;
00215 }
00216 if (!cap_ask && shutdownMode == KWorkSpace::ShutdownModeInteractive)
00217 shutdownMode = KWorkSpace::ShutdownModeForceNow;
00218
00219 QByteArray cmd;
00220 if (DMType == GDM) {
00221 cmd.append( shutdownMode == KWorkSpace::ShutdownModeForceNow ?
00222 "SET_LOGOUT_ACTION " : "SET_SAFE_LOGOUT_ACTION " );
00223 cmd.append( shutdownType == KWorkSpace::ShutdownTypeReboot ?
00224 "REBOOT\n" : "HALT\n" );
00225 } else {
00226 cmd.append( "shutdown\t" );
00227 cmd.append( shutdownType == KWorkSpace::ShutdownTypeReboot ?
00228 "reboot\t" : "halt\t" );
00229 if (!bootOption.isEmpty())
00230 cmd.append( "=" ).append( bootOption.toLocal8Bit() ).append( "\t" );
00231 cmd.append( shutdownMode == KWorkSpace::ShutdownModeInteractive ?
00232 "ask\n" :
00233 shutdownMode == KWorkSpace::ShutdownModeForceNow ?
00234 "forcenow\n" :
00235 shutdownMode == KWorkSpace::ShutdownModeTryNow ?
00236 "trynow\n" : "schedule\n" );
00237 }
00238 exec( cmd.data() );
00239 }
00240
00241 bool
00242 KDisplayManager::bootOptions( QStringList &opts, int &defopt, int ¤t )
00243 {
00244 if (DMType != NewKDM)
00245 return false;
00246
00247 QByteArray re;
00248 if (!exec( "listbootoptions\n", re ))
00249 return false;
00250
00251 opts = QString::fromLocal8Bit( re.data() ).split( '\t', QString::SkipEmptyParts );
00252 if (opts.size() < 4)
00253 return false;
00254
00255 bool ok;
00256 defopt = opts[2].toInt( &ok );
00257 if (!ok)
00258 return false;
00259 current = opts[3].toInt( &ok );
00260 if (!ok)
00261 return false;
00262
00263 opts = opts[1].split( ' ', QString::SkipEmptyParts );
00264 for (QStringList::Iterator it = opts.begin(); it != opts.end(); ++it)
00265 (*it).replace( "\\s", " " );
00266
00267 return true;
00268 }
00269
00270 void
00271 KDisplayManager::setLock( bool on )
00272 {
00273 if (DMType != GDM)
00274 exec( on ? "lock\n" : "unlock\n" );
00275 }
00276
00277 bool
00278 KDisplayManager::isSwitchable()
00279 {
00280 if (DMType == OldKDM)
00281 return dpy[0] == ':';
00282
00283 if (DMType == GDM)
00284 return exec( "QUERY_VT\n" );
00285
00286 QByteArray re;
00287
00288 return exec( "caps\n", re ) && re.indexOf( "\tlocal" ) >= 0;
00289 }
00290
00291 int
00292 KDisplayManager::numReserve()
00293 {
00294 if (DMType == GDM)
00295 return 1;
00296
00297 if (DMType == OldKDM)
00298 return strstr( ctl, ",rsvd" ) ? 1 : -1;
00299
00300 QByteArray re;
00301 int p;
00302
00303 if (!(exec( "caps\n", re ) && (p = re.indexOf( "\treserve " )) >= 0))
00304 return -1;
00305 return atoi( re.data() + p + 9 );
00306 }
00307
00308 void
00309 KDisplayManager::startReserve()
00310 {
00311 if (DMType == GDM)
00312 exec("FLEXI_XSERVER\n");
00313 else
00314 exec("reserve\n");
00315 }
00316
00317 bool
00318 KDisplayManager::localSessions( SessList &list )
00319 {
00320 if (DMType == OldKDM)
00321 return false;
00322
00323 QByteArray re;
00324
00325 if (DMType == GDM) {
00326 if (!exec( "CONSOLE_SERVERS\n", re ))
00327 return false;
00328 const QStringList sess = QString(re.data() +3).split( QChar(';'), QString::SkipEmptyParts);
00329 for (QStringList::ConstIterator it = sess.constBegin(); it != sess.constEnd(); ++it) {
00330 QStringList ts = (*it).split( QChar(',') );
00331 SessEnt se;
00332 se.display = ts[0];
00333 se.user = ts[1];
00334 se.vt = ts[2].toInt();
00335 se.session = "<unknown>";
00336 se.self = ts[0] == ::getenv( "DISPLAY" );
00337 se.tty = false;
00338 list.append( se );
00339 }
00340 } else {
00341 if (!exec( "list\talllocal\n", re ))
00342 return false;
00343 const QStringList sess = QString(re.data() + 3).split(QChar('\t'), QString::SkipEmptyParts );
00344 for (QStringList::ConstIterator it = sess.constBegin(); it != sess.constEnd(); ++it) {
00345 QStringList ts = (*it).split( QChar(',') );
00346 SessEnt se;
00347 se.display = ts[0];
00348 if (ts[1][0] == '@')
00349 se.from = ts[1].mid( 1 ), se.vt = 0;
00350 else
00351 se.vt = ts[1].mid( 2 ).toInt();
00352 se.user = ts[2];
00353 se.session = ts[3];
00354 se.self = (ts[4].indexOf( '*' ) >= 0);
00355 se.tty = (ts[4].indexOf( 't' ) >= 0);
00356 list.append( se );
00357 }
00358 }
00359 return true;
00360 }
00361
00362 void
00363 KDisplayManager::sess2Str2( const SessEnt &se, QString &user, QString &loc )
00364 {
00365 if (se.tty) {
00366 user = i18nc("user: ...", "%1: TTY login", se.user );
00367 loc = se.vt ? QString("vt%1").arg( se.vt ) : se.display ;
00368 } else {
00369 user =
00370 se.user.isEmpty() ?
00371 se.session.isEmpty() ?
00372 i18nc("... location (TTY or X display)", "Unused") :
00373 se.session == "<remote>" ?
00374 i18n("X login on remote host") :
00375 i18nc("... host", "X login on %1", se.session ) :
00376 se.session == "<unknown>" ?
00377 se.user :
00378 i18nc("user: session type", "%1: %2",
00379 se.user, se.session );
00380 loc =
00381 se.vt ?
00382 QString("%1, vt%2").arg( se.display ).arg( se.vt ) :
00383 se.display;
00384 }
00385 }
00386
00387 QString
00388 KDisplayManager::sess2Str( const SessEnt &se )
00389 {
00390 QString user, loc;
00391
00392 sess2Str2( se, user, loc );
00393 return i18nc("session (location)", "%1 (%2)", user, loc );
00394 }
00395
00396 bool
00397 KDisplayManager::switchVT( int vt )
00398 {
00399 if (DMType == GDM)
00400 return exec( QString("SET_VT %1\n").arg(vt).toLatin1() );
00401
00402 return exec( QString("activate\tvt%1\n").arg(vt).toLatin1() );
00403 }
00404
00405 void
00406 KDisplayManager::lockSwitchVT( int vt )
00407 {
00408 if (switchVT( vt ))
00409 {
00410 QDBusInterface screensaver("org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver");
00411 screensaver.call( "Lock" );
00412 }
00413 }
00414
00415 void
00416 KDisplayManager::GDMAuthenticate()
00417 {
00418 FILE *fp;
00419 const char *dpy, *dnum, *dne;
00420 int dnl;
00421 Xauth *xau;
00422
00423 dpy = DisplayString( QX11Info::display() );
00424 if (!dpy) {
00425 dpy = ::getenv( "DISPLAY" );
00426 if (!dpy)
00427 return;
00428 }
00429 dnum = strchr( dpy, ':' ) + 1;
00430 dne = strchr( dpy, '.' );
00431 dnl = dne ? dne - dnum : strlen( dnum );
00432
00433
00434 if (!(fp = fopen( XauFileName(), "r" )))
00435 return;
00436
00437 while ((xau = XauReadAuth( fp ))) {
00438 if (xau->family == FamilyLocal &&
00439 xau->number_length == dnl && !memcmp( xau->number, dnum, dnl ) &&
00440 xau->data_length == 16 &&
00441 xau->name_length == 18 && !memcmp( xau->name, "MIT-MAGIC-COOKIE-1", 18 ))
00442 {
00443 QString cmd( "AUTH_LOCAL " );
00444 for (int i = 0; i < 16; i++)
00445 cmd += QString::number( (uchar)xau->data[i], 16 ).rightJustified( 2, '0');
00446 cmd += '\n';
00447 if (exec( cmd.toLatin1() )) {
00448 XauDisposeAuth( xau );
00449 break;
00450 }
00451 }
00452 XauDisposeAuth( xau );
00453 }
00454
00455 fclose (fp);
00456 }
00457
00458 #endif // Q_WS_X11