mirror of
https://bitbucket.org/smil3y/kde-workspace.git
synced 2025-02-24 10:52:51 +00:00
893 lines
30 KiB
C++
893 lines
30 KiB
C++
/*
|
|
Copyright (C) 2004 Oswald Buddenhagen <ossi@kde.org>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the Lesser GNU General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the Lesser GNU General Public License
|
|
along with this program; see the file COPYING. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "kdisplaymanager.h"
|
|
|
|
#ifdef Q_WS_X11
|
|
|
|
#include <kapplication.h>
|
|
#include <klocale.h>
|
|
#include <kuser.h>
|
|
|
|
#include <QtDBus/QtDBus>
|
|
#include <QRegExp>
|
|
|
|
#include <X11/Xauth.h>
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <stdio.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(
|
|
QLatin1String("org.freedesktop.ConsoleKit"),
|
|
QLatin1String("/org/freedesktop/ConsoleKit/Manager"),
|
|
QLatin1String("org.freedesktop.ConsoleKit.Manager"),
|
|
QDBusConnection::systemBus()) {}
|
|
};
|
|
|
|
class CKSeat : public QDBusInterface
|
|
{
|
|
public:
|
|
CKSeat(const QDBusObjectPath &path) :
|
|
QDBusInterface(
|
|
QLatin1String("org.freedesktop.ConsoleKit"),
|
|
path.path(),
|
|
QLatin1String("org.freedesktop.ConsoleKit.Seat"),
|
|
QDBusConnection::systemBus()) {}
|
|
};
|
|
|
|
class CKSession : public QDBusInterface
|
|
{
|
|
public:
|
|
CKSession(const QDBusObjectPath &path) :
|
|
QDBusInterface(
|
|
QLatin1String("org.freedesktop.ConsoleKit"),
|
|
path.path(),
|
|
QLatin1String("org.freedesktop.ConsoleKit.Session"),
|
|
QDBusConnection::systemBus()) {}
|
|
void getSessionLocation(SessEnt &se)
|
|
{
|
|
QString tty;
|
|
QDBusReply<QString> r = call(QLatin1String("GetX11Display"));
|
|
if (r.isValid() && !r.value().isEmpty()) {
|
|
QDBusReply<QString> r2 = call(QLatin1String("GetX11DisplayDevice"));
|
|
tty = r2.value();
|
|
se.display = r.value();
|
|
se.tty = false;
|
|
} else {
|
|
QDBusReply<QString> r2 = call(QLatin1String("GetDisplayDevice"));
|
|
tty = r2.value();
|
|
se.display = tty;
|
|
se.tty = true;
|
|
}
|
|
se.vt = tty.mid(strlen("/dev/tty")).toInt();
|
|
}
|
|
};
|
|
|
|
class GDMFactory : public QDBusInterface
|
|
{
|
|
public:
|
|
GDMFactory() :
|
|
QDBusInterface(
|
|
QLatin1String("org.gnome.DisplayManager"),
|
|
QLatin1String("/org/gnome/DisplayManager/LocalDisplayFactory"),
|
|
QLatin1String("org.gnome.DisplayManager.LocalDisplayFactory"),
|
|
QDBusConnection::systemBus()) {}
|
|
};
|
|
|
|
class LightDMDBus : public QDBusInterface
|
|
{
|
|
public:
|
|
LightDMDBus() :
|
|
QDBusInterface(
|
|
QLatin1String("org.freedesktop.DisplayManager"),
|
|
qgetenv("XDG_SEAT_PATH"),
|
|
QLatin1String("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) {
|
|
if (!(dpy = ::getenv("DISPLAY")))
|
|
DMType = NoDM;
|
|
else if ((ctl = ::getenv("DM_CONTROL")))
|
|
DMType = NewKDM;
|
|
else if ((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 addional 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;
|
|
QDBusReply<QDBusObjectPath> r = man.call(QLatin1String("GetSessionByPID"), (uint) QCoreApplication::applicationPid());
|
|
if (r.isValid()) {
|
|
SystemdSession sess(r.value());
|
|
if (sess.isValid()) {
|
|
NamedDBusObjectPath namedPath = sess.getSeat();
|
|
if (currentSession)
|
|
*currentSession = r.value();
|
|
*currentSeat = namedPath.path;
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
CKManager man;
|
|
QDBusReply<QDBusObjectPath> r = man.call(QLatin1String("GetCurrentSession"));
|
|
if (r.isValid()) {
|
|
CKSession sess(r.value());
|
|
if (sess.isValid()) {
|
|
QDBusReply<QDBusObjectPath> r2 = sess.call(QLatin1String("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("/org/freedesktop/ConsoleKit")) {
|
|
CKSeat seat(path);
|
|
if (seat.isValid()) {
|
|
QDBusReply<QList<QDBusObjectPath> > r = seat.call(QLatin1String("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(QLatin1String("CanPowerOff"));
|
|
if (canPowerOff.isValid())
|
|
return canPowerOff.value() != QLatin1String("no");
|
|
QDBusReply<bool> canStop = CKManager().call(QLatin1String("CanStop"));
|
|
if (canStop.isValid())
|
|
return canStop.value();
|
|
return false;
|
|
}
|
|
|
|
if (DMType == OldKDM)
|
|
return strstr(ctl, ",maysd") != 0;
|
|
|
|
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());
|
|
}
|
|
#endif // KDM_NO_SHUTDOWN
|
|
|
|
// This only tells KDM to not auto-re-login upon session crash
|
|
void
|
|
KDisplayManager::setLock(bool on)
|
|
{
|
|
if (DMType == NewKDM || DMType == OldKDM)
|
|
exec(on ? "lock\n" : "unlock\n");
|
|
}
|
|
|
|
bool
|
|
KDisplayManager::isSwitchable()
|
|
{
|
|
if (DMType == NewGDM || DMType == LightDM) {
|
|
QDBusObjectPath currentSeat;
|
|
if (getCurrentSeat(0, ¤tSeat)) {
|
|
SystemdSeat SDseat(currentSeat);
|
|
if (SDseat.isValid()) {
|
|
QVariant prop = SDseat.property("CanMultiSession");
|
|
if (prop.isValid())
|
|
return prop.toBool();
|
|
QDBusInterface systemdIface(
|
|
"org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager",
|
|
QDBusConnection::systemBus()
|
|
);
|
|
QString systemdversion = systemdIface.property("Version").toString();
|
|
const int dotindex = systemdversion.indexOf(QLatin1Char('.'));
|
|
if (dotindex > 0) {
|
|
systemdversion = systemdversion.left(dotindex);
|
|
}
|
|
// always allowed since 246 and the CanMultiSession property is hidden
|
|
if (systemdversion.toLongLong() >= 246) {
|
|
return true;
|
|
}
|
|
}
|
|
CKSeat CKseat(currentSeat);
|
|
if (CKseat.isValid()) {
|
|
QDBusReply<bool> r = CKseat.call(QLatin1String("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(QLatin1String("CreateTransientDisplay"));
|
|
else if (DMType == OldGDM)
|
|
exec("FLEXI_XSERVER\n");
|
|
else if (DMType == LightDM) {
|
|
LightDMDBus lightDM;
|
|
lightDM.call("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(¤tSession, ¤tSeat)) {
|
|
// 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 = "<unknown>";
|
|
se.self = lsess.property("Display").toString() == ::getenv("DISPLAY"); /* Bleh once again */
|
|
se.tty = !lsess.property("TTY").toString().isEmpty();
|
|
}
|
|
list.append(se);
|
|
}
|
|
}
|
|
}
|
|
// ConsoleKit part
|
|
else if (QDBusConnection::systemBus().interface()->isServiceRegistered("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(QLatin1String("GetSessionType"));
|
|
if (r.value() != QLatin1String("LoginWindow")) {
|
|
QDBusReply<unsigned> r2 = lsess.call(QLatin1String("GetUnixUser"));
|
|
se.user = KUser(K_UID(r2.value())).loginName();
|
|
se.session = "<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(';'), QString::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 = "<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'), QString::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].mid(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 ? QString("vt%1").arg(se.vt) : se.display ;
|
|
} else {
|
|
user =
|
|
se.user.isEmpty() ?
|
|
se.session.isEmpty() ?
|
|
i18nc("... location (TTY or X display)", "Unused") :
|
|
se.session == "<remote>" ?
|
|
i18n("X login on remote host") :
|
|
i18nc("... host", "X login on %1", se.session) :
|
|
se.session == "<unknown>" ?
|
|
se.user :
|
|
i18nc("user: session type", "%1: %2",
|
|
se.user, se.session);
|
|
loc =
|
|
se.vt ?
|
|
QString("%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(0, ¤tSeat)) {
|
|
// 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("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(QLatin1String("Activate"));
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (DMType == OldGDM)
|
|
return exec(QString("SET_VT %1\n").arg(vt).toLatin1());
|
|
|
|
return exec(QString("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("org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver");
|
|
screensaver.call("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("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 // Q_WS_X11
|