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"
24
#include <kapplication.h>
28
#include <QtDBus/QtDBus>
31
#include <X11/Xauth.h>
34
#include <sys/types.h>
35
#include <sys/socket.h>
43
class CKManager : public QDBusInterface
48
QLatin1String("org.freedesktop.ConsoleKit"),
49
QLatin1String("/org/freedesktop/ConsoleKit/Manager"),
50
QLatin1String("org.freedesktop.ConsoleKit.Manager"),
51
QDBusConnection::systemBus()) {}
54
class CKSeat : public QDBusInterface
57
CKSeat(const QDBusObjectPath &path) :
59
QLatin1String("org.freedesktop.ConsoleKit"),
61
QLatin1String("org.freedesktop.ConsoleKit.Seat"),
62
QDBusConnection::systemBus()) {}
65
class CKSession : public QDBusInterface
68
CKSession(const QDBusObjectPath &path) :
70
QLatin1String("org.freedesktop.ConsoleKit"),
72
QLatin1String("org.freedesktop.ConsoleKit.Session"),
73
QDBusConnection::systemBus()) {}
76
class GDMFactory : public QDBusInterface
81
QLatin1String("org.gnome.DisplayManager"),
82
QLatin1String("/org/gnome/DisplayManager/LocalDisplayFactory"),
83
QLatin1String("org.gnome.DisplayManager.LocalDisplayFactory"),
84
QDBusConnection::systemBus()) {}
87
class LightDMDBus : public QDBusInterface
92
QLatin1String("org.lightdm.LightDisplayManager"),
93
"/org/lightdm/LightDisplayManager",
94
QLatin1String("org.lightdm.LightDisplayManager"),
95
QDBusConnection::systemBus()) {}
98
static enum { Dunno, NoDM, NewKDM, OldKDM, NewGDM, OldGDM, LightDM } DMType = Dunno;
99
static const char *ctl, *dpy;
101
class KDisplayManager::Private
104
Private() : fd(-1) {}
113
KDisplayManager::KDisplayManager() : d(new Private)
116
struct sockaddr_un sa;
118
if (DMType == Dunno) {
119
if (!(dpy = ::getenv("DISPLAY")))
121
else if ((ctl = ::getenv("DM_CONTROL")))
123
else if ((ctl = ::getenv("XDM_MANAGED")) && ctl[0] == '/')
125
else if (::getenv("GDMSESSION")) {
126
//lightDM identifies itself as GDM at the moment.
127
QDBusReply<bool> reply = QDBusConnection::systemBus().interface()->isServiceRegistered("org.lightdm.LightDisplayManager");
128
if (reply.isValid()) {
131
DMType = GDMFactory().isValid() ? NewGDM : OldGDM;
142
if ((d->fd = ::socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
144
sa.sun_family = AF_UNIX;
145
if (DMType == OldGDM) {
146
strcpy(sa.sun_path, "/var/run/gdm_socket");
147
if (::connect(d->fd, (struct sockaddr *)&sa, sizeof(sa))) {
148
strcpy(sa.sun_path, "/tmp/.gdm_socket");
149
if (::connect(d->fd, (struct sockaddr *)&sa, sizeof(sa))) {
157
if ((ptr = strchr(dpy, ':')))
158
ptr = strchr(ptr, '.');
159
snprintf(sa.sun_path, sizeof(sa.sun_path),
160
"%s/dmctl-%.*s/socket",
161
ctl, ptr ? int(ptr - dpy) : 512, dpy);
162
if (::connect(d->fd, (struct sockaddr *)&sa, sizeof(sa))) {
171
tf.truncate(tf.indexOf(','));
172
d->fd = ::open(tf.toLatin1(), O_WRONLY);
178
KDisplayManager::~KDisplayManager()
184
KDisplayManager::exec(const char *cmd)
188
return exec(cmd, buf);
192
* Execute a KDM/GDM remote control command.
193
* @param cmd the command to execute. FIXME: undocumented yet.
194
* @param buf the result buffer.
196
* @li If true, the command was successfully executed.
197
* @p ret might contain addional results.
198
* @li If false and @p ret is empty, a communication error occurred
199
* (most probably KDM is not running).
200
* @li If false and @p ret is non-empty, it contains the error message
204
KDisplayManager::exec(const char *cmd, QByteArray &buf)
214
if (::write(d->fd, cmd, tl) != tl) {
222
if (DMType == OldKDM) {
227
if (buf.size() < 128)
229
else if (buf.size() < len * 2)
231
if ((tl = ::read(d->fd, buf.data() + len, buf.size() - len)) <= 0) {
232
if (tl < 0 && errno == EINTR)
237
if (buf[len - 1] == '\n') {
239
if (len > 2 && (buf[0] == 'o' || buf[0] == 'O') &&
240
(buf[1] == 'k' || buf[1] == 'K') && buf[2] <= ' ')
248
static bool getCurrentSeat(QDBusObjectPath *currentSession, QDBusObjectPath *currentSeat)
251
QDBusReply<QDBusObjectPath> r = man.call(QLatin1String("GetCurrentSession"));
253
CKSession sess(r.value());
254
if (sess.isValid()) {
255
QDBusReply<QDBusObjectPath> r2 = sess.call(QLatin1String("GetSeatId"));
258
*currentSession = r.value();
259
*currentSeat = r2.value();
267
static QList<QDBusObjectPath> getSessionsForSeat(const QDBusObjectPath &path)
270
if (seat.isValid()) {
271
QDBusReply<QList<QDBusObjectPath> > r = seat.call(QLatin1String("GetSessions"));
273
// This will contain only local sessions:
274
// - this is only ever called when isSwitchable() is true => local seat
275
// - remote logins into the machine are assigned to other seats
279
return QList<QDBusObjectPath>();
282
static void getSessionLocation(CKSession &lsess, SessEnt &se)
285
QDBusReply<QString> r = lsess.call(QLatin1String("GetX11Display"));
286
if (r.isValid() && !r.value().isEmpty()) {
287
QDBusReply<QString> r2 = lsess.call(QLatin1String("GetX11DisplayDevice"));
289
se.display = r.value();
292
QDBusReply<QString> r2 = lsess.call(QLatin1String("GetDisplayDevice"));
297
se.vt = tty.mid(strlen("/dev/tty")).toInt();
300
#ifndef KDM_NO_SHUTDOWN
302
KDisplayManager::canShutdown()
304
if (DMType == NewGDM || DMType == NoDM || DMType == LightDM) {
305
QDBusReply<bool> canStop = CKManager().call(QLatin1String("CanStop"));
306
return (canStop.isValid() && canStop.value());
309
if (DMType == OldKDM)
310
return strstr(ctl, ",maysd") != 0;
314
if (DMType == OldGDM)
315
return exec("QUERY_LOGOUT_ACTION\n", re) && re.indexOf("HALT") >= 0;
317
return exec("caps\n", re) && re.indexOf("\tshutdown") >= 0;
321
KDisplayManager::shutdown(KWorkSpace::ShutdownType shutdownType,
322
KWorkSpace::ShutdownMode shutdownMode, /* NOT Default */
323
const QString &bootOption)
325
if (shutdownType == KWorkSpace::ShutdownTypeNone || shutdownType == KWorkSpace::ShutdownTypeLogout)
329
if (DMType == NewKDM) {
331
cap_ask = exec("caps\n", re) && re.indexOf("\tshutdown ask") >= 0;
333
if (!bootOption.isEmpty())
336
if (DMType == NewGDM || DMType == NoDM || DMType == LightDM) {
337
// FIXME: entirely ignoring shutdownMode
338
CKManager().call(QLatin1String(
339
shutdownType == KWorkSpace::ShutdownTypeReboot ? "Restart" : "Stop"));
345
if (!cap_ask && shutdownMode == KWorkSpace::ShutdownModeInteractive)
346
shutdownMode = KWorkSpace::ShutdownModeForceNow;
349
if (DMType == OldGDM) {
350
cmd.append(shutdownMode == KWorkSpace::ShutdownModeForceNow ?
351
"SET_LOGOUT_ACTION " : "SET_SAFE_LOGOUT_ACTION ");
352
cmd.append(shutdownType == KWorkSpace::ShutdownTypeReboot ?
353
"REBOOT\n" : "HALT\n");
355
cmd.append("shutdown\t");
356
cmd.append(shutdownType == KWorkSpace::ShutdownTypeReboot ?
357
"reboot\t" : "halt\t");
358
if (!bootOption.isEmpty())
359
cmd.append("=").append(bootOption.toLocal8Bit()).append("\t");
360
cmd.append(shutdownMode == KWorkSpace::ShutdownModeInteractive ?
362
shutdownMode == KWorkSpace::ShutdownModeForceNow ?
364
shutdownMode == KWorkSpace::ShutdownModeTryNow ?
365
"trynow\n" : "schedule\n");
371
KDisplayManager::bootOptions(QStringList &opts, int &defopt, int ¤t)
373
if (DMType != NewKDM)
377
if (!exec("listbootoptions\n", re))
380
opts = QString::fromLocal8Bit(re.data()).split('\t', QString::SkipEmptyParts);
385
defopt = opts[2].toInt(&ok);
388
current = opts[3].toInt(&ok);
392
opts = opts[1].split(' ', QString::SkipEmptyParts);
393
for (QStringList::Iterator it = opts.begin(); it != opts.end(); ++it)
394
(*it).replace("\\s", " ");
398
#endif // KDM_NO_SHUTDOWN
400
// This only tells KDM to not auto-re-login upon session crash
402
KDisplayManager::setLock(bool on)
404
if (DMType == NewKDM || DMType == OldKDM)
405
exec(on ? "lock\n" : "unlock\n");
409
KDisplayManager::isSwitchable()
411
if (DMType == NewGDM || DMType == LightDM) {
412
QDBusObjectPath currentSeat;
413
if (getCurrentSeat(0, ¤tSeat)) {
414
CKSeat seat(currentSeat);
415
if (seat.isValid()) {
416
QDBusReply<bool> r = seat.call(QLatin1String("CanActivateSessions"));
424
if (DMType == OldKDM)
425
return dpy[0] == ':';
427
if (DMType == OldGDM)
428
return exec("QUERY_VT\n");
432
return exec("caps\n", re) && re.indexOf("\tlocal") >= 0;
436
KDisplayManager::numReserve()
438
if (DMType == NewGDM || DMType == OldGDM || DMType == LightDM)
441
if (DMType == OldKDM)
442
return strstr(ctl, ",rsvd") ? 1 : -1;
447
if (!(exec("caps\n", re) && (p = re.indexOf("\treserve ")) >= 0))
449
return atoi(re.data() + p + 9);
453
KDisplayManager::startReserve()
455
if (DMType == NewGDM)
456
GDMFactory().call(QLatin1String("CreateTransientDisplay"));
457
else if (DMType == OldGDM)
458
exec("FLEXI_XSERVER\n");
459
else if (DMType == LightDM) {
461
lightDM.call("SwitchToUser", QVariant::fromValue<QString>(""));
468
KDisplayManager::localSessions(SessList &list)
470
if (DMType == OldKDM)
473
if (DMType == NewGDM || DMType == LightDM) {
474
QDBusObjectPath currentSession, currentSeat;
475
if (getCurrentSeat(¤tSession, ¤tSeat)) {
476
foreach (const QDBusObjectPath &sp, getSessionsForSeat(currentSeat)) {
478
if (lsess.isValid()) {
480
getSessionLocation(lsess, se);
481
// "Warning: we haven't yet defined the allowed values for this property.
482
// It is probably best to avoid this until we do."
483
QDBusReply<QString> r = lsess.call(QLatin1String("GetSessionType"));
484
if (r.value() != QLatin1String("LoginWindow")) {
485
QDBusReply<unsigned> r2 = lsess.call(QLatin1String("GetUnixUser"));
486
se.user = KUser(K_UID(r2.value())).loginName();
487
se.session = "<unknown>";
489
se.self = (sp == currentSession);
500
if (DMType == OldGDM) {
501
if (!exec("CONSOLE_SERVERS\n", re))
503
const QStringList sess = QString(re.data() +3).split(QChar(';'), QString::SkipEmptyParts);
504
for (QStringList::ConstIterator it = sess.constBegin(); it != sess.constEnd(); ++it) {
505
QStringList ts = (*it).split(QChar(','));
509
se.vt = ts[2].toInt();
510
se.session = "<unknown>";
511
se.self = ts[0] == ::getenv("DISPLAY"); /* Bleh */
516
if (!exec("list\talllocal\n", re))
518
const QStringList sess = QString(re.data() + 3).split(QChar('\t'), QString::SkipEmptyParts);
519
for (QStringList::ConstIterator it = sess.constBegin(); it != sess.constEnd(); ++it) {
520
QStringList ts = (*it).split(QChar(','));
523
se.vt = ts[1].mid(2).toInt();
526
se.self = (ts[4].indexOf('*') >= 0);
527
se.tty = (ts[4].indexOf('t') >= 0);
535
KDisplayManager::sess2Str2(const SessEnt &se, QString &user, QString &loc)
538
user = i18nc("user: ...", "%1: TTY login", se.user);
539
loc = se.vt ? QString("vt%1").arg(se.vt) : se.display ;
543
se.session.isEmpty() ?
544
i18nc("... location (TTY or X display)", "Unused") :
545
se.session == "<remote>" ?
546
i18n("X login on remote host") :
547
i18nc("... host", "X login on %1", se.session) :
548
se.session == "<unknown>" ?
550
i18nc("user: session type", "%1: %2",
551
se.user, se.session);
554
QString("%1, vt%2").arg(se.display).arg(se.vt) :
560
KDisplayManager::sess2Str(const SessEnt &se)
564
sess2Str2(se, user, loc);
565
return i18nc("session (location)", "%1 (%2)", user, loc);
569
KDisplayManager::switchVT(int vt)
571
if (DMType == NewGDM || DMType == LightDM) {
572
QDBusObjectPath currentSeat;
573
if (getCurrentSeat(0, ¤tSeat)) {
574
foreach (const QDBusObjectPath &sp, getSessionsForSeat(currentSeat)) {
576
if (lsess.isValid()) {
578
getSessionLocation(lsess, se);
580
if (se.tty) // ConsoleKit simply ignores these
582
lsess.call(QLatin1String("Activate"));
591
if (DMType == OldGDM)
592
return exec(QString("SET_VT %1\n").arg(vt).toLatin1());
594
return exec(QString("activate\tvt%1\n").arg(vt).toLatin1());
598
KDisplayManager::lockSwitchVT(int vt)
601
QDBusInterface screensaver("org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver");
602
screensaver.call("Lock");
607
KDisplayManager::GDMAuthenticate()
610
const char *dpy, *dnum, *dne;
614
dpy = DisplayString(QX11Info::display());
616
dpy = ::getenv("DISPLAY");
620
dnum = strchr(dpy, ':') + 1;
621
dne = strchr(dpy, '.');
622
dnl = dne ? dne - dnum : strlen(dnum);
624
/* XXX should do locking */
625
if (!(fp = fopen(XauFileName(), "r")))
628
while ((xau = XauReadAuth(fp))) {
629
if (xau->family == FamilyLocal &&
630
xau->number_length == dnl && !memcmp(xau->number, dnum, dnl) &&
631
xau->data_length == 16 &&
632
xau->name_length == 18 && !memcmp(xau->name, "MIT-MAGIC-COOKIE-1", 18))
634
QString cmd("AUTH_LOCAL ");
635
for (int i = 0; i < 16; i++)
636
cmd += QString::number((uchar)xau->data[i], 16).rightJustified(2, '0');
638
if (exec(cmd.toLatin1())) {