plasma-workspace/libkworkspace/kdisplaymanager.cpp

885 lines
30 KiB
C++

/*
SPDX-FileCopyrightText: 2004 Oswald Buddenhagen <ossi@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kdisplaymanager.h"
#if HAVE_X11
#include <kuser.h>
#include <KLocalizedString>
#include <QCoreApplication>
#include <QDBusArgument>
#include <QDBusConnectionInterface>
#include <QDBusInterface>
#include <QDBusMetaType>
#include <QDBusObjectPath>
#include <QDBusReply>
#include <QX11Info>
#include <X11/Xauth.h>
#include <X11/Xlib.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#define _DBUS_PROPERTIES_IFACE "org.freedesktop.DBus.Properties"
#define _DBUS_PROPERTIES_GET "Get"
#define DBUS_PROPERTIES_IFACE QLatin1String(_DBUS_PROPERTIES_IFACE)
#define DBUS_PROPERTIES_GET QLatin1String(_DBUS_PROPERTIES_GET)
#define _SYSTEMD_SERVICE "org.freedesktop.login1"
#define _SYSTEMD_BASE_PATH "/org/freedesktop/login1"
#define _SYSTEMD_MANAGER_IFACE _SYSTEMD_SERVICE ".Manager"
#define _SYSTEMD_SESSION_BASE_PATH _SYSTEMD_BASE_PATH "/session"
#define _SYSTEMD_SEAT_IFACE _SYSTEMD_SERVICE ".Seat"
#define _SYSTEMD_SEAT_BASE_PATH _SYSTEMD_BASE_PATH "/seat"
#define _SYSTEMD_SESSION_IFACE _SYSTEMD_SERVICE ".Session"
#define _SYSTEMD_USER_PROPERTY "User"
#define _SYSTEMD_SEAT_PROPERTY "Seat"
#define _SYSTEMD_SESSIONS_PROPERTY "Sessions"
#define _SYSTEMD_SWITCH_PROPERTY "Activate"
#define SYSTEMD_SERVICE QLatin1String(_SYSTEMD_SERVICE)
#define SYSTEMD_BASE_PATH QLatin1String(_SYSTEMD_BASE_PATH)
#define SYSTEMD_MANAGER_IFACE QLatin1String(_SYSTEMD_MANAGER_IFACE)
#define SYSTEMD_SESSION_BASE_PATH QLatin1String(_SYSTEMD_SESSION_BASE_PATH)
#define SYSTEMD_SEAT_IFACE QLatin1String(_SYSTEMD_SEAT_IFACE)
#define SYSTEMD_SEAT_BASE_PATH QLatin1String(_SYSTEMD_SEAT_BASE_PATH)
#define SYSTEMD_SESSION_IFACE QLatin1String(_SYSTEMD_SESSION_IFACE)
#define SYSTEMD_USER_PROPERTY QLatin1String(_SYSTEMD_USER_PROPERTY)
#define SYSTEMD_SEAT_PROPERTY QLatin1String(_SYSTEMD_SEAT_PROPERTY)
#define SYSTEMD_SESSIONS_PROPERTY QLatin1String(_SYSTEMD_SESSIONS_PROPERTY)
#define SYSTEMD_SWITCH_CALL QLatin1String(_SYSTEMD_SWITCH_PROPERTY)
struct NamedDBusObjectPath {
QString name;
QDBusObjectPath path;
};
Q_DECLARE_METATYPE(NamedDBusObjectPath)
Q_DECLARE_METATYPE(QList<NamedDBusObjectPath>)
// Marshall the NamedDBusObjectPath data into a D-Bus argument
QDBusArgument &operator<<(QDBusArgument &argument, const NamedDBusObjectPath &namedPath)
{
argument.beginStructure();
argument << namedPath.name << namedPath.path;
argument.endStructure();
return argument;
}
// Retrieve the NamedDBusObjectPath data from the D-Bus argument
const QDBusArgument &operator>>(const QDBusArgument &argument, NamedDBusObjectPath &namedPath)
{
argument.beginStructure();
argument >> namedPath.name >> namedPath.path;
argument.endStructure();
return argument;
}
struct NumberedDBusObjectPath {
uint num;
QDBusObjectPath path;
};
Q_DECLARE_METATYPE(NumberedDBusObjectPath)
// Marshall the NumberedDBusObjectPath data into a D-Bus argument
QDBusArgument &operator<<(QDBusArgument &argument, const NumberedDBusObjectPath &numberedPath)
{
argument.beginStructure();
argument << numberedPath.num << numberedPath.path;
argument.endStructure();
return argument;
}
// Retrieve the NumberedDBusObjectPath data from the D-Bus argument
const QDBusArgument &operator>>(const QDBusArgument &argument, NumberedDBusObjectPath &numberedPath)
{
argument.beginStructure();
argument >> numberedPath.num >> numberedPath.path;
argument.endStructure();
return argument;
}
class SystemdManager : public QDBusInterface
{
public:
SystemdManager()
: QDBusInterface(SYSTEMD_SERVICE, SYSTEMD_BASE_PATH, SYSTEMD_MANAGER_IFACE, QDBusConnection::systemBus())
{
}
};
class SystemdSeat : public QDBusInterface
{
public:
SystemdSeat(const QDBusObjectPath &path)
: QDBusInterface(SYSTEMD_SERVICE, path.path(), SYSTEMD_SEAT_IFACE, QDBusConnection::systemBus())
{
}
/* HACK to be able to extract a(so) type from QDBus, property doesn't do the trick */
QList<NamedDBusObjectPath> getSessions()
{
QDBusMessage message = QDBusMessage::createMethodCall(service(), path(), DBUS_PROPERTIES_IFACE, DBUS_PROPERTIES_GET);
message << interface() << SYSTEMD_SESSIONS_PROPERTY;
QDBusMessage reply = QDBusConnection::systemBus().call(message);
QVariantList args = reply.arguments();
if (!args.isEmpty()) {
QList<NamedDBusObjectPath> namedPathList =
qdbus_cast<QList<NamedDBusObjectPath>>(args.at(0).value<QDBusVariant>().variant().value<QDBusArgument>());
return namedPathList;
}
return QList<NamedDBusObjectPath>();
}
};
class SystemdSession : public QDBusInterface
{
public:
SystemdSession(const QDBusObjectPath &path)
: QDBusInterface(SYSTEMD_SERVICE, path.path(), SYSTEMD_SESSION_IFACE, QDBusConnection::systemBus())
{
}
/* HACK to be able to extract (so) type from QDBus, property doesn't do the trick */
NamedDBusObjectPath getSeat()
{
QDBusMessage message = QDBusMessage::createMethodCall(service(), path(), DBUS_PROPERTIES_IFACE, DBUS_PROPERTIES_GET);
message << interface() << SYSTEMD_SEAT_PROPERTY;
QDBusMessage reply = QDBusConnection::systemBus().call(message);
QVariantList args = reply.arguments();
if (!args.isEmpty()) {
NamedDBusObjectPath namedPath;
args.at(0).value<QDBusVariant>().variant().value<QDBusArgument>() >> namedPath;
return namedPath;
}
return NamedDBusObjectPath();
}
NumberedDBusObjectPath getUser()
{
QDBusMessage message = QDBusMessage::createMethodCall(service(), path(), DBUS_PROPERTIES_IFACE, DBUS_PROPERTIES_GET);
message << interface() << SYSTEMD_USER_PROPERTY;
QDBusMessage reply = QDBusConnection::systemBus().call(message);
QVariantList args = reply.arguments();
if (!args.isEmpty()) {
NumberedDBusObjectPath numberedPath;
args.at(0).value<QDBusVariant>().variant().value<QDBusArgument>() >> numberedPath;
return numberedPath;
}
return NumberedDBusObjectPath();
}
void getSessionLocation(SessEnt &se)
{
se.tty = (property("Type").toString() != QLatin1String("x11"));
se.display = property(se.tty ? "TTY" : "Display").toString();
se.vt = property("VTNr").toInt();
}
};
class CKManager : public QDBusInterface
{
public:
CKManager()
: QDBusInterface(QStringLiteral("org.freedesktop.ConsoleKit"),
QStringLiteral("/org/freedesktop/ConsoleKit/Manager"),
QStringLiteral("org.freedesktop.ConsoleKit.Manager"),
QDBusConnection::systemBus())
{
}
};
class CKSeat : public QDBusInterface
{
public:
CKSeat(const QDBusObjectPath &path)
: QDBusInterface(QStringLiteral("org.freedesktop.ConsoleKit"),
path.path(),
QStringLiteral("org.freedesktop.ConsoleKit.Seat"),
QDBusConnection::systemBus())
{
}
};
class CKSession : public QDBusInterface
{
public:
CKSession(const QDBusObjectPath &path)
: QDBusInterface(QStringLiteral("org.freedesktop.ConsoleKit"),
path.path(),
QStringLiteral("org.freedesktop.ConsoleKit.Session"),
QDBusConnection::systemBus())
{
}
void getSessionLocation(SessEnt &se)
{
QString tty;
QDBusReply<QString> r = call(QStringLiteral("GetX11Display"));
if (r.isValid() && !r.value().isEmpty()) {
QDBusReply<QString> r2 = call(QStringLiteral("GetX11DisplayDevice"));
tty = r2.value();
se.display = r.value();
se.tty = false;
} else {
QDBusReply<QString> r2 = call(QStringLiteral("GetDisplayDevice"));
tty = r2.value();
se.display = tty;
se.tty = true;
}
se.vt = tty.midRef(strlen("/dev/tty")).toInt();
}
};
class GDMFactory : public QDBusInterface
{
public:
GDMFactory()
: QDBusInterface(QStringLiteral("org.gnome.DisplayManager"),
QStringLiteral("/org/gnome/DisplayManager/LocalDisplayFactory"),
QStringLiteral("org.gnome.DisplayManager.LocalDisplayFactory"),
QDBusConnection::systemBus())
{
}
};
class LightDMDBus : public QDBusInterface
{
public:
LightDMDBus()
: QDBusInterface(QStringLiteral("org.freedesktop.DisplayManager"),
qgetenv("XDG_SEAT_PATH"),
QStringLiteral("org.freedesktop.DisplayManager.Seat"),
QDBusConnection::systemBus())
{
}
};
static enum {
Dunno,
NoDM,
NewKDM,
OldKDM,
NewGDM,
OldGDM,
LightDM,
} DMType = Dunno;
static const char *ctl, *dpy;
class KDisplayManager::Private
{
public:
Private()
: fd(-1)
{
}
~Private()
{
if (fd >= 0)
close(fd);
}
int fd;
};
KDisplayManager::KDisplayManager()
: d(new Private)
{
const char *ptr;
struct sockaddr_un sa;
qDBusRegisterMetaType<NamedDBusObjectPath>();
qDBusRegisterMetaType<QList<NamedDBusObjectPath>>();
qDBusRegisterMetaType<NumberedDBusObjectPath>();
if (DMType == Dunno) {
dpy = ::getenv("DISPLAY");
if (dpy && (ctl = ::getenv("DM_CONTROL")))
DMType = NewKDM;
else if (dpy && (ctl = ::getenv("XDM_MANAGED")) && ctl[0] == '/')
DMType = OldKDM;
else if (::getenv("XDG_SEAT_PATH") && LightDMDBus().isValid())
DMType = LightDM;
else if (::getenv("GDMSESSION"))
DMType = GDMFactory().isValid() ? NewGDM : OldGDM;
else
DMType = NoDM;
}
switch (DMType) {
default:
return;
case NewKDM:
case OldGDM:
if ((d->fd = ::socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
return;
sa.sun_family = AF_UNIX;
if (DMType == OldGDM) {
strcpy(sa.sun_path, "/var/run/gdm_socket");
if (::connect(d->fd, (struct sockaddr *)&sa, sizeof(sa))) {
strcpy(sa.sun_path, "/tmp/.gdm_socket");
if (::connect(d->fd, (struct sockaddr *)&sa, sizeof(sa))) {
::close(d->fd);
d->fd = -1;
break;
}
}
GDMAuthenticate();
} else {
if ((ptr = strchr(dpy, ':')))
ptr = strchr(ptr, '.');
snprintf(sa.sun_path, sizeof(sa.sun_path), "%s/dmctl-%.*s/socket", ctl, ptr ? int(ptr - dpy) : 512, dpy);
if (::connect(d->fd, (struct sockaddr *)&sa, sizeof(sa))) {
::close(d->fd);
d->fd = -1;
}
}
break;
case OldKDM: {
QString tf(ctl);
tf.truncate(tf.indexOf(','));
d->fd = ::open(tf.toLatin1(), O_WRONLY);
} break;
}
}
KDisplayManager::~KDisplayManager()
{
delete d;
}
bool KDisplayManager::exec(const char *cmd)
{
QByteArray buf;
return exec(cmd, buf);
}
/**
* Execute a KDM/GDM remote control command.
* @param cmd the command to execute. FIXME: undocumented yet.
* @param buf the result buffer.
* @return result:
* @li If true, the command was successfully executed.
* @p ret might contain additional results.
* @li If false and @p ret is empty, a communication error occurred
* (most probably KDM is not running).
* @li If false and @p ret is non-empty, it contains the error message
* from KDM.
*/
bool KDisplayManager::exec(const char *cmd, QByteArray &buf)
{
bool ret = false;
int tl;
int len = 0;
if (d->fd < 0)
goto busted;
tl = strlen(cmd);
if (::write(d->fd, cmd, tl) != tl) {
bust:
::close(d->fd);
d->fd = -1;
busted:
buf.resize(0);
return false;
}
if (DMType == OldKDM) {
buf.resize(0);
return true;
}
for (;;) {
if (buf.size() < 128)
buf.resize(128);
else if (buf.size() < len * 2)
buf.resize(len * 2);
if ((tl = ::read(d->fd, buf.data() + len, buf.size() - len)) <= 0) {
if (tl < 0 && errno == EINTR)
continue;
goto bust;
}
len += tl;
if (buf[len - 1] == '\n') {
buf[len - 1] = 0;
if (len > 2 && (buf[0] == 'o' || buf[0] == 'O') && (buf[1] == 'k' || buf[1] == 'K') && buf[2] <= ' ')
ret = true;
break;
}
}
return ret;
}
static bool getCurrentSeat(QDBusObjectPath *currentSession, QDBusObjectPath *currentSeat)
{
SystemdManager man;
if (man.isValid()) {
*currentSeat = QDBusObjectPath(_SYSTEMD_SEAT_BASE_PATH "/auto");
SystemdSeat seat(*currentSeat);
if (seat.property("Id").isValid()) { // query an arbitrary property to confirm the path is valid
return true;
}
// auto is newer and may not exist on all platforms, fallback to GetSessionByPID if the above failed
QDBusReply<QDBusObjectPath> r = man.call(QStringLiteral("GetSessionByPID"), (uint)QCoreApplication::applicationPid());
if (r.isValid()) {
SystemdSession sess(r.value());
if (sess.isValid()) {
NamedDBusObjectPath namedPath = sess.getSeat();
*currentSeat = namedPath.path;
return true;
}
}
} else {
CKManager man;
QDBusReply<QDBusObjectPath> r = man.call(QStringLiteral("GetCurrentSession"));
if (r.isValid()) {
CKSession sess(r.value());
if (sess.isValid()) {
QDBusReply<QDBusObjectPath> r2 = sess.call(QStringLiteral("GetSeatId"));
if (r2.isValid()) {
if (currentSession)
*currentSession = r.value();
*currentSeat = r2.value();
return true;
}
}
}
}
return false;
}
static QList<QDBusObjectPath> getSessionsForSeat(const QDBusObjectPath &path)
{
if (path.path().startsWith(SYSTEMD_BASE_PATH)) { // systemd path incoming
SystemdSeat seat(path);
if (seat.isValid()) {
QList<NamedDBusObjectPath> r = seat.getSessions();
QList<QDBusObjectPath> result;
foreach (const NamedDBusObjectPath &namedPath, r)
result.append(namedPath.path);
// This pretty much can't contain any other than local sessions as the seat is retrieved from the current session
return result;
}
} else if (path.path().startsWith(QLatin1String("/org/freedesktop/ConsoleKit"))) {
CKSeat seat(path);
if (seat.isValid()) {
QDBusReply<QList<QDBusObjectPath>> r = seat.call(QStringLiteral("GetSessions"));
if (r.isValid()) {
// This will contain only local sessions:
// - this is only ever called when isSwitchable() is true => local seat
// - remote logins into the machine are assigned to other seats
return r.value();
}
}
}
return QList<QDBusObjectPath>();
}
#ifndef KDM_NO_SHUTDOWN
bool KDisplayManager::canShutdown()
{
if (DMType == NewGDM || DMType == NoDM || DMType == LightDM) {
QDBusReply<QString> canPowerOff = SystemdManager().call(QStringLiteral("CanPowerOff"));
if (canPowerOff.isValid())
return canPowerOff.value() != QLatin1String("no");
QDBusReply<bool> canStop = CKManager().call(QStringLiteral("CanStop"));
if (canStop.isValid())
return canStop.value();
return false;
}
if (DMType == OldKDM)
return strstr(ctl, ",maysd") != nullptr;
QByteArray re;
if (DMType == OldGDM)
return exec("QUERY_LOGOUT_ACTION\n", re) && re.indexOf("HALT") >= 0;
return exec("caps\n", re) && re.indexOf("\tshutdown") >= 0;
}
void KDisplayManager::shutdown(KWorkSpace::ShutdownType shutdownType,
KWorkSpace::ShutdownMode shutdownMode, /* NOT Default */
const QString &bootOption)
{
if (shutdownType == KWorkSpace::ShutdownTypeNone || shutdownType == KWorkSpace::ShutdownTypeLogout)
return;
bool cap_ask;
if (DMType == NewKDM) {
QByteArray re;
cap_ask = exec("caps\n", re) && re.indexOf("\tshutdown ask") >= 0;
} else {
if (!bootOption.isEmpty())
return;
if (DMType == NewGDM || DMType == NoDM || DMType == LightDM) {
// systemd supports only 2 modes:
// * interactive = true: brings up a PolicyKit prompt if other sessions are active
// * interactive = false: rejects the shutdown if other sessions are active
// There are no schedule or force modes.
// We try to map our 4 shutdown modes in the sanest way.
bool interactive = (shutdownMode == KWorkSpace::ShutdownModeInteractive || shutdownMode == KWorkSpace::ShutdownModeForceNow);
QDBusReply<QString> check =
SystemdManager().call(QLatin1String(shutdownType == KWorkSpace::ShutdownTypeReboot ? "Reboot" : "PowerOff"), interactive);
if (!check.isValid()) {
// FIXME: entirely ignoring shutdownMode
CKManager().call(QLatin1String(shutdownType == KWorkSpace::ShutdownTypeReboot ? "Restart" : "Stop"));
// if even CKManager call fails, there is nothing more to be done
}
return;
}
cap_ask = false;
}
if (!cap_ask && shutdownMode == KWorkSpace::ShutdownModeInteractive)
shutdownMode = KWorkSpace::ShutdownModeForceNow;
QByteArray cmd;
if (DMType == OldGDM) {
cmd.append(shutdownMode == KWorkSpace::ShutdownModeForceNow ? "SET_LOGOUT_ACTION " : "SET_SAFE_LOGOUT_ACTION ");
cmd.append(shutdownType == KWorkSpace::ShutdownTypeReboot ? "REBOOT\n" : "HALT\n");
} else {
cmd.append("shutdown\t");
cmd.append(shutdownType == KWorkSpace::ShutdownTypeReboot ? "reboot\t" : "halt\t");
if (!bootOption.isEmpty())
cmd.append("=").append(bootOption.toLocal8Bit()).append("\t");
cmd.append(shutdownMode == KWorkSpace::ShutdownModeInteractive ? "ask\n"
: shutdownMode == KWorkSpace::ShutdownModeForceNow ? "forcenow\n"
: shutdownMode == KWorkSpace::ShutdownModeTryNow ? "trynow\n"
: "schedule\n");
}
exec(cmd.data());
}
bool KDisplayManager::bootOptions(QStringList &opts, int &defopt, int &current)
{
if (DMType != NewKDM)
return false;
QByteArray re;
if (!exec("listbootoptions\n", re))
return false;
opts = QString::fromLocal8Bit(re.data()).split('\t', Qt::SkipEmptyParts);
if (opts.size() < 4)
return false;
bool ok;
defopt = opts[2].toInt(&ok);
if (!ok)
return false;
current = opts[3].toInt(&ok);
if (!ok)
return false;
opts = opts[1].split(' ', Qt::SkipEmptyParts);
for (QStringList::Iterator it = opts.begin(); it != opts.end(); ++it)
(*it).replace(QLatin1String("\\s"), QLatin1String(" "));
return true;
}
#endif // KDM_NO_SHUTDOWN
bool KDisplayManager::isSwitchable()
{
if (DMType == NewGDM || DMType == LightDM) {
QDBusObjectPath currentSeat;
if (getCurrentSeat(nullptr, &currentSeat)) {
SystemdSeat SDseat(currentSeat);
if (SDseat.isValid()) {
QVariant prop = SDseat.property("CanMultiSession");
if (prop.isValid())
return prop.toBool();
else {
// Newer systemd versions (since 246) don't expose "CanMultiSession" anymore.
// It's hidden and always true.
// See https://github.com/systemd/systemd/commit/8f8cc84ba4612e74cd1e26898c6816e6e60fc4e9
// and https://github.com/systemd/systemd/commit/c2b178d3cacad52eadc30ecc349160bc02d32a9c
// So assume that it's supported if the property is invalid.
return true;
}
}
CKSeat CKseat(currentSeat);
if (CKseat.isValid()) {
QDBusReply<bool> r = CKseat.call(QStringLiteral("CanActivateSessions"));
if (r.isValid())
return r.value();
}
}
return false;
}
if (DMType == OldKDM)
return dpy[0] == ':';
if (DMType == OldGDM)
return exec("QUERY_VT\n");
QByteArray re;
return exec("caps\n", re) && re.indexOf("\tlocal") >= 0;
}
int KDisplayManager::numReserve()
{
if (DMType == NewGDM || DMType == OldGDM || DMType == LightDM)
return 1; /* Bleh */
if (DMType == OldKDM)
return strstr(ctl, ",rsvd") ? 1 : -1;
QByteArray re;
int p;
if (!(exec("caps\n", re) && (p = re.indexOf("\treserve ")) >= 0))
return -1;
return atoi(re.data() + p + 9);
}
void KDisplayManager::startReserve()
{
if (DMType == NewGDM)
GDMFactory().call(QStringLiteral("CreateTransientDisplay"));
else if (DMType == OldGDM)
exec("FLEXI_XSERVER\n");
else if (DMType == LightDM) {
LightDMDBus lightDM;
lightDM.call(QStringLiteral("SwitchToGreeter"));
} else
exec("reserve\n");
}
bool KDisplayManager::localSessions(SessList &list)
{
if (DMType == OldKDM)
return false;
if (DMType == NewGDM || DMType == LightDM) {
QDBusObjectPath currentSession, currentSeat;
if (getCurrentSeat(&currentSession, &currentSeat)) {
// we'll divide the code in two branches to reduce the overhead of calls to non-existent services
// systemd part // preferred
if (QDBusConnection::systemBus().interface()->isServiceRegistered(SYSTEMD_SERVICE)) {
foreach (const QDBusObjectPath &sp, getSessionsForSeat(currentSeat)) {
SystemdSession lsess(sp);
if (lsess.isValid()) {
SessEnt se;
lsess.getSessionLocation(se);
if ((lsess.property("Class").toString() != QLatin1String("greeter"))
&& (lsess.property("State").toString() == QLatin1String("online")
|| lsess.property("State").toString() == QLatin1String("active"))) {
NumberedDBusObjectPath numberedPath = lsess.getUser();
se.display = lsess.property("Display").toString();
se.vt = lsess.property("VTNr").toInt();
se.user = KUser(K_UID(numberedPath.num)).loginName();
/* TODO:
* regarding the session name in this, it IS possible to find it out - logind tracks the session leader PID
* the problem is finding out the name of the process, I could come only with reading /proc/PID/comm which
* doesn't seem exactly... right to me --mbriza
*/
se.session = QStringLiteral("<unknown>");
se.self = lsess.property("Id").toString() == qgetenv("XDG_SESSION_ID");
se.tty = !lsess.property("TTY").toString().isEmpty();
}
list.append(se);
}
}
}
// ConsoleKit part
else if (QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.ConsoleKit"))) {
foreach (const QDBusObjectPath &sp, getSessionsForSeat(currentSeat)) {
CKSession lsess(sp);
if (lsess.isValid()) {
SessEnt se;
lsess.getSessionLocation(se);
// "Warning: we haven't yet defined the allowed values for this property.
// It is probably best to avoid this until we do."
QDBusReply<QString> r = lsess.call(QStringLiteral("GetSessionType"));
if (r.value() != QLatin1String("LoginWindow")) {
QDBusReply<unsigned> r2 = lsess.call(QStringLiteral("GetUnixUser"));
se.user = KUser(K_UID(r2.value())).loginName();
se.session = QStringLiteral("<unknown>");
}
se.self = (sp == currentSession);
list.append(se);
}
}
} else {
return false;
}
return true;
}
return false;
}
QByteArray re;
if (DMType == OldGDM) {
if (!exec("CONSOLE_SERVERS\n", re))
return false;
const QStringList sess = QString(re.data() + 3).split(QChar(';'), Qt::SkipEmptyParts);
for (QStringList::ConstIterator it = sess.constBegin(); it != sess.constEnd(); ++it) {
QStringList ts = (*it).split(QChar(','));
SessEnt se;
se.display = ts[0];
se.user = ts[1];
se.vt = ts[2].toInt();
se.session = QStringLiteral("<unknown>");
se.self = ts[0] == ::getenv("DISPLAY"); /* Bleh */
se.tty = false;
list.append(se);
}
} else {
if (!exec("list\talllocal\n", re))
return false;
const QStringList sess = QString(re.data() + 3).split(QChar('\t'), Qt::SkipEmptyParts);
for (QStringList::ConstIterator it = sess.constBegin(); it != sess.constEnd(); ++it) {
QStringList ts = (*it).split(QChar(','));
SessEnt se;
se.display = ts[0];
se.vt = ts[1].midRef(2).toInt();
se.user = ts[2];
se.session = ts[3];
se.self = (ts[4].indexOf('*') >= 0);
se.tty = (ts[4].indexOf('t') >= 0);
list.append(se);
}
}
return true;
}
void KDisplayManager::sess2Str2(const SessEnt &se, QString &user, QString &loc)
{
if (se.tty) {
user = i18nc("user: …", "%1: TTY login", se.user);
loc = se.vt ? QStringLiteral("vt%1").arg(se.vt) : se.display;
} else {
// clang-format off
user = se.user.isEmpty() ? se.session.isEmpty()
? i18nc("… location (TTY or X display)", "Unused") : se.session == QLatin1String("<remote>")
? i18n("X login on remote host") : i18nc("… host", "X login on %1", se.session)
: se.session == QLatin1String("<unknown>")
? se.user : i18nc("user: session type", "%1: %2", se.user, se.session);
// clang-format on
loc = se.vt ? QStringLiteral("%1, vt%2").arg(se.display).arg(se.vt) : se.display;
}
}
QString KDisplayManager::sess2Str(const SessEnt &se)
{
QString user, loc;
sess2Str2(se, user, loc);
return i18nc("session (location)", "%1 (%2)", user, loc);
}
bool KDisplayManager::switchVT(int vt)
{
if (DMType == NewGDM || DMType == LightDM) {
QDBusObjectPath currentSeat;
if (getCurrentSeat(nullptr, &currentSeat)) {
// systemd part // preferred
if (QDBusConnection::systemBus().interface()->isServiceRegistered(SYSTEMD_SERVICE)) {
foreach (const QDBusObjectPath &sp, getSessionsForSeat(currentSeat)) {
SystemdSession lsess(sp);
if (lsess.isValid()) {
SessEnt se;
lsess.getSessionLocation(se);
if (se.vt == vt) {
lsess.call(SYSTEMD_SWITCH_CALL);
return true;
}
}
}
}
// ConsoleKit part
else if (QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.ConsoleKit"))) {
foreach (const QDBusObjectPath &sp, getSessionsForSeat(currentSeat)) {
CKSession lsess(sp);
if (lsess.isValid()) {
SessEnt se;
lsess.getSessionLocation(se);
if (se.vt == vt) {
if (se.tty) // ConsoleKit simply ignores these
return false;
lsess.call(QStringLiteral("Activate"));
return true;
}
}
}
}
}
return false;
}
if (DMType == OldGDM)
return exec(QStringLiteral("SET_VT %1\n").arg(vt).toLatin1());
return exec(QStringLiteral("activate\tvt%1\n").arg(vt).toLatin1());
}
void KDisplayManager::lockSwitchVT(int vt)
{
// Lock first, otherwise the lock won't be able to kick in until the session is re-activated.
QDBusInterface screensaver(QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("/ScreenSaver"), QStringLiteral("org.freedesktop.ScreenSaver"));
screensaver.call(QStringLiteral("Lock"));
switchVT(vt);
}
void KDisplayManager::GDMAuthenticate()
{
FILE *fp;
const char *dpy, *dnum, *dne;
int dnl;
Xauth *xau;
dpy = DisplayString(QX11Info::display());
if (!dpy) {
dpy = ::getenv("DISPLAY");
if (!dpy)
return;
}
dnum = strchr(dpy, ':') + 1;
dne = strchr(dpy, '.');
dnl = dne ? dne - dnum : strlen(dnum);
/* XXX should do locking */
if (!(fp = fopen(XauFileName(), "r")))
return;
while ((xau = XauReadAuth(fp))) {
if (xau->family == FamilyLocal && xau->number_length == dnl && !memcmp(xau->number, dnum, dnl) && xau->data_length == 16 && xau->name_length == 18
&& !memcmp(xau->name, "MIT-MAGIC-COOKIE-1", 18)) {
QString cmd(QStringLiteral("AUTH_LOCAL "));
for (int i = 0; i < 16; i++)
cmd += QString::number((uchar)xau->data[i], 16).rightJustified(2, '0');
cmd += '\n';
if (exec(cmd.toLatin1())) {
XauDisposeAuth(xau);
break;
}
}
XauDisposeAuth(xau);
}
fclose(fp);
}
#endif // HAVE_X11