File systemd-session-management-support-in-the-workspace.patch of Package kdebase4-workspace
--- a/libs/kworkspace/kdisplaymanager.cpp
+++ b/libs/kworkspace/kdisplaymanager.cpp
@@ -40,6 +40,166 @@
#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:
@@ -68,9 +228,26 @@
CKSession(const QDBusObjectPath &path) :
QDBusInterface(
QLatin1String("org.freedesktop.ConsoleKit"),
- path.path(),
+ 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
@@ -114,6 +291,10 @@
{
const char *ptr;
struct sockaddr_un sa;
+
+ qDBusRegisterMetaType<NamedDBusObjectPath>();
+ qDBusRegisterMetaType<QList<NamedDBusObjectPath> >();
+ qDBusRegisterMetaType<NumberedDBusObjectPath>();
if (DMType == Dunno) {
if (!(dpy = ::getenv("DISPLAY")))
@@ -242,17 +423,31 @@
static bool getCurrentSeat(QDBusObjectPath *currentSession, QDBusObjectPath *currentSeat)
{
- CKManager man;
- QDBusReply<QDBusObjectPath> r = man.call(QLatin1String("GetCurrentSession"));
+ SystemdManager man;
+ QDBusReply<QDBusObjectPath> r = man.call(QLatin1String("GetSessionByPID"), (uint) QCoreApplication::applicationPid());
if (r.isValid()) {
- CKSession sess(r.value());
+ SystemdSession 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;
+ 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;
+ }
}
}
}
@@ -261,35 +456,30 @@
static QList<QDBusObjectPath> getSessionsForSeat(const QDBusObjectPath &path)
{
- 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();
+ 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>();
-}
-
-static void getSessionLocation(CKSession &lsess, SessEnt &se)
-{
- QString tty;
- QDBusReply<QString> r = lsess.call(QLatin1String("GetX11Display"));
- if (r.isValid() && !r.value().isEmpty()) {
- QDBusReply<QString> r2 = lsess.call(QLatin1String("GetX11DisplayDevice"));
- tty = r2.value();
- se.display = r.value();
- se.tty = false;
- } else {
- QDBusReply<QString> r2 = lsess.call(QLatin1String("GetDisplayDevice"));
- tty = r2.value();
- se.display = tty;
- se.tty = true;
- }
- se.vt = tty.mid(strlen("/dev/tty")).toInt();
}
#ifndef KDM_NO_SHUTDOWN
@@ -297,8 +487,13 @@
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"));
- return (canStop.isValid() && canStop.value());
+ if (canStop.isValid())
+ return canStop.value();
+ return false;
}
if (DMType == OldKDM)
@@ -329,9 +524,21 @@
return;
if (DMType == NewGDM || DMType == NoDM || DMType == LightDM) {
- // FIXME: entirely ignoring shutdownMode
- CKManager().call(QLatin1String(
- shutdownType == KWorkSpace::ShutdownTypeReboot ? "Restart" : "Stop"));
+ // 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;
}
@@ -406,9 +613,15 @@
if (DMType == NewGDM || DMType == LightDM) {
QDBusObjectPath currentSeat;
if (getCurrentSeat(0, ¤tSeat)) {
- CKSeat seat(currentSeat);
- if (seat.isValid()) {
- QDBusReply<bool> r = seat.call(QLatin1String("CanActivateSessions"));
+ SystemdSeat SDseat(currentSeat);
+ if (SDseat.isValid()) {
+ QVariant prop = SDseat.property("CanMultiSession");
+ if (prop.isValid())
+ return prop.toBool();
+ }
+ CKSeat CKseat(currentSeat);
+ if (CKseat.isValid()) {
+ QDBusReply<bool> r = CKseat.call(QLatin1String("CanActivateSessions"));
if (r.isValid())
return r.value();
}
@@ -468,22 +681,56 @@
if (DMType == NewGDM || DMType == LightDM) {
QDBusObjectPath currentSession, currentSeat;
if (getCurrentSeat(¤tSession, ¤tSeat)) {
- foreach (const QDBusObjectPath &sp, getSessionsForSeat(currentSeat)) {
- CKSession lsess(sp);
- if (lsess.isValid()) {
- SessEnt se;
- getSessionLocation(lsess, 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>";
+ // 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);
}
- se.self = (sp == currentSession);
- 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;
}
@@ -566,16 +813,33 @@
if (DMType == NewGDM || DMType == LightDM) {
QDBusObjectPath currentSeat;
if (getCurrentSeat(0, ¤tSeat)) {
- foreach (const QDBusObjectPath &sp, getSessionsForSeat(currentSeat)) {
- CKSession lsess(sp);
- if (lsess.isValid()) {
- SessEnt se;
- getSessionLocation(lsess, se);
- if (se.vt == vt) {
- if (se.tty) // ConsoleKit simply ignores these
- return false;
- lsess.call(QLatin1String("Activate"));
- return true;
+ // 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;
+ }
}
}
}