7#include "kdisplaymanager.h"
11#include <KLocalizedString>
13#include <QDBusArgument>
14#include <QDBusConnectionInterface>
15#include <QDBusInterface>
16#include <QDBusMetaType>
17#include <QDBusObjectPath>
19#include <QGuiApplication>
21#include "config-X11.h"
31#include <sys/socket.h>
36#define _DBUS_PROPERTIES_IFACE "org.freedesktop.DBus.Properties"
37#define _DBUS_PROPERTIES_GET "Get"
39#define DBUS_PROPERTIES_IFACE QLatin1String(_DBUS_PROPERTIES_IFACE)
40#define DBUS_PROPERTIES_GET QLatin1String(_DBUS_PROPERTIES_GET)
42#define _SYSTEMD_SERVICE "org.freedesktop.login1"
43#define _SYSTEMD_BASE_PATH "/org/freedesktop/login1"
44#define _SYSTEMD_MANAGER_IFACE _SYSTEMD_SERVICE ".Manager"
45#define _SYSTEMD_SESSION_BASE_PATH _SYSTEMD_BASE_PATH "/session"
46#define _SYSTEMD_SEAT_IFACE _SYSTEMD_SERVICE ".Seat"
47#define _SYSTEMD_SEAT_BASE_PATH _SYSTEMD_BASE_PATH "/seat"
48#define _SYSTEMD_SESSION_IFACE _SYSTEMD_SERVICE ".Session"
49#define _SYSTEMD_USER_PROPERTY "User"
50#define _SYSTEMD_SEAT_PROPERTY "Seat"
51#define _SYSTEMD_SESSIONS_PROPERTY "Sessions"
52#define _SYSTEMD_SWITCH_PROPERTY "Activate"
54#define SYSTEMD_SERVICE QLatin1String(_SYSTEMD_SERVICE)
55#define SYSTEMD_BASE_PATH QLatin1String(_SYSTEMD_BASE_PATH)
56#define SYSTEMD_MANAGER_IFACE QLatin1String(_SYSTEMD_MANAGER_IFACE)
57#define SYSTEMD_SESSION_BASE_PATH QLatin1String(_SYSTEMD_SESSION_BASE_PATH)
58#define SYSTEMD_SEAT_IFACE QLatin1String(_SYSTEMD_SEAT_IFACE)
59#define SYSTEMD_SEAT_BASE_PATH QLatin1String(_SYSTEMD_SEAT_BASE_PATH)
60#define SYSTEMD_SESSION_IFACE QLatin1String(_SYSTEMD_SESSION_IFACE)
61#define SYSTEMD_USER_PROPERTY QLatin1String(_SYSTEMD_USER_PROPERTY)
62#define SYSTEMD_SEAT_PROPERTY QLatin1String(_SYSTEMD_SEAT_PROPERTY)
63#define SYSTEMD_SESSIONS_PROPERTY QLatin1String(_SYSTEMD_SESSIONS_PROPERTY)
64#define SYSTEMD_SWITCH_CALL QLatin1String(_SYSTEMD_SWITCH_PROPERTY)
66struct NamedDBusObjectPath {
75 argument << namedPath.name << namedPath.path;
84 argument >> namedPath.name >> namedPath.path;
89struct NumberedDBusObjectPath {
98 argument << numberedPath.num << numberedPath.path;
107 argument >> numberedPath.num >> numberedPath.path;
116 :
QDBusInterface(SYSTEMD_SERVICE, SYSTEMD_BASE_PATH, SYSTEMD_MANAGER_IFACE, QDBusConnection::systemBus())
124 SystemdSeat(
const QDBusObjectPath &
path)
129 QList<NamedDBusObjectPath> getSessions()
132 message <<
interface() << SYSTEMD_SESSIONS_PROPERTY;
136 if (!args.isEmpty()) {
137 QList<NamedDBusObjectPath> namedPathList =
138 qdbus_cast<QList<NamedDBusObjectPath>>(args.at(0).value<QDBusVariant>().variant().value<QDBusArgument>());
139 return namedPathList;
141 return QList<NamedDBusObjectPath>();
148 SystemdSession(
const QDBusObjectPath &
path)
153 NamedDBusObjectPath getSeat()
156 message <<
interface() << SYSTEMD_SEAT_PROPERTY;
160 if (!args.isEmpty()) {
161 NamedDBusObjectPath namedPath;
162 args.at(0).value<QDBusVariant>().variant().value<QDBusArgument>() >> namedPath;
165 return NamedDBusObjectPath();
167 NumberedDBusObjectPath getUser()
170 message <<
interface() << SYSTEMD_USER_PROPERTY;
174 if (!args.isEmpty()) {
175 NumberedDBusObjectPath numberedPath;
176 args.at(0).value<QDBusVariant>().variant().value<QDBusArgument>() >> numberedPath;
179 return NumberedDBusObjectPath();
181 void getSessionLocation(SessEnt &se)
194 QStringLiteral(
"/org/freedesktop/ConsoleKit/Manager"),
195 QStringLiteral(
"org.freedesktop.ConsoleKit.Manager"),
196 QDBusConnection::systemBus())
204 CKSeat(
const QDBusObjectPath &
path)
207 QStringLiteral(
"org.freedesktop.ConsoleKit.Seat"),
208 QDBusConnection::systemBus())
216 CKSession(
const QDBusObjectPath &
path)
219 QStringLiteral(
"org.freedesktop.ConsoleKit.Session"),
220 QDBusConnection::systemBus())
223 void getSessionLocation(SessEnt &se)
226 QDBusReply<QString> r =
call(QStringLiteral(
"GetX11Display"));
227 if (r.
isValid() && !r.value().isEmpty()) {
228 QDBusReply<QString> r2 =
call(QStringLiteral(
"GetX11DisplayDevice"));
230 se.display = r.value();
233 QDBusReply<QString> r2 =
call(QStringLiteral(
"GetDisplayDevice"));
238 se.vt = QStringView(tty).mid(strlen(
"/dev/tty")).toInt();
247 QStringLiteral(
"/org/gnome/DisplayManager/LocalDisplayFactory"),
248 QStringLiteral(
"org.gnome.DisplayManager.LocalDisplayFactory"),
249 QDBusConnection::systemBus())
258 :
QDBusInterface(QStringLiteral(
"org.freedesktop.DisplayManager"),
259 qEnvironmentVariable(
"XDG_SEAT_PATH"),
260 QStringLiteral(
"org.freedesktop.DisplayManager.Seat"),
261 QDBusConnection::systemBus())
275static const char *ctl, *dpy;
277class KDisplayManager::Private
293KDisplayManager::KDisplayManager()
297 struct sockaddr_un sa;
299 qDBusRegisterMetaType<NamedDBusObjectPath>();
300 qDBusRegisterMetaType<QList<NamedDBusObjectPath>>();
301 qDBusRegisterMetaType<NumberedDBusObjectPath>();
303 if (DMType == Dunno) {
304 dpy = ::getenv(
"DISPLAY");
305 if (dpy && (ctl = ::getenv(
"DM_CONTROL")))
307 else if (dpy && (ctl = ::getenv(
"XDM_MANAGED")) && ctl[0] ==
'/')
309 else if (::getenv(
"XDG_SEAT_PATH") && LightDMDBus().
isValid())
311 else if (::getenv(
"GDMSESSION"))
312 DMType = GDMFactory().isValid() ? NewGDM : OldGDM;
321 if ((d->fd = ::socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
323 sa.sun_family = AF_UNIX;
324 if (DMType == OldGDM) {
325 strcpy(sa.sun_path,
"/var/run/gdm_socket");
326 if (::connect(d->fd, (
struct sockaddr *)&sa,
sizeof(sa))) {
327 strcpy(sa.sun_path,
"/tmp/.gdm_socket");
328 if (::connect(d->fd, (
struct sockaddr *)&sa,
sizeof(sa))) {
336 if ((ptr = strchr(dpy,
':')))
337 ptr = strchr(ptr,
'.');
338 snprintf(sa.sun_path,
sizeof(sa.sun_path),
"%s/dmctl-%.*s/socket", ctl, ptr ?
int(ptr - dpy) : 512, dpy);
339 if (::connect(d->fd, (
struct sockaddr *)&sa,
sizeof(sa))) {
347 tf.truncate(tf.indexOf(
','));
348 d->fd = ::open(tf.constData(), O_WRONLY);
353KDisplayManager::~KDisplayManager()
358bool KDisplayManager::exec(
const char *cmd)
362 return exec(cmd, buf);
377bool KDisplayManager::exec(
const char *cmd,
QByteArray &buf)
387 if (::write(d->fd, cmd, tl) != tl) {
395 if (DMType == OldKDM) {
400 if (buf.
size() < 128)
402 else if (buf.
size() < len * 2)
404 if ((tl = ::read(d->fd, buf.
data() + len, buf.
size() - len)) <= 0) {
405 if (tl < 0 && errno == EINTR)
410 if (buf[len - 1] ==
'\n') {
412 if (len > 2 && (buf[0] ==
'o' || buf[0] ==
'O') && (buf[1] ==
'k' || buf[1] ==
'K') && buf[2] <=
' ')
425 SystemdSeat seat(*currentSeat);
426 if (seat.property(
"Id").isValid()) {
434 SystemdSession sess(r.value());
435 if (sess.isValid()) {
436 NamedDBusObjectPath namedPath = sess.getSeat();
437 *currentSeat = namedPath.path;
445 CKSession sess(r.value());
446 if (sess.isValid()) {
450 *currentSession = r.value();
451 *currentSeat = r2.value();
463 SystemdSeat seat(path);
464 if (seat.isValid()) {
467 for (
const NamedDBusObjectPath &namedPath : r)
468 result.
append(namedPath.path);
474 if (seat.isValid()) {
487#ifndef KDM_NO_SHUTDOWN
488bool KDisplayManager::canShutdown()
490 if (DMType == NewGDM || DMType == NoDM || DMType == LightDM) {
491 QDBusReply<QString> canPowerOff = SystemdManager().call(QStringLiteral(
"CanPowerOff"));
493 return canPowerOff.value() != QLatin1String(
"no");
494 QDBusReply<bool> canStop = CKManager().call(QStringLiteral(
"CanStop"));
496 return canStop.value();
500 if (DMType == OldKDM)
501 return strstr(ctl,
",maysd") !=
nullptr;
505 if (DMType == OldGDM)
506 return exec(
"QUERY_LOGOUT_ACTION\n", re) && re.
indexOf(
"HALT") >= 0;
508 return exec(
"caps\n", re) && re.
indexOf(
"\tshutdown") >= 0;
511void KDisplayManager::shutdown(KWorkSpace::ShutdownType shutdownType,
512 KWorkSpace::ShutdownMode shutdownMode,
515 if (shutdownType == KWorkSpace::ShutdownTypeNone || shutdownType == KWorkSpace::ShutdownTypeLogout)
519 if (DMType == NewKDM) {
521 cap_ask = exec(
"caps\n", re) && re.
indexOf(
"\tshutdown ask") >= 0;
526 if (DMType == NewGDM || DMType == NoDM || DMType == LightDM) {
532 bool interactive = (shutdownMode == KWorkSpace::ShutdownModeInteractive || shutdownMode == KWorkSpace::ShutdownModeForceNow);
533 QDBusReply<QString> check =
534 SystemdManager().call(QLatin1String(shutdownType == KWorkSpace::ShutdownTypeReboot ?
"Reboot" :
"PowerOff"), interactive);
537 CKManager().call(QLatin1String(shutdownType == KWorkSpace::ShutdownTypeReboot ?
"Restart" :
"Stop"));
545 if (!cap_ask && shutdownMode == KWorkSpace::ShutdownModeInteractive)
546 shutdownMode = KWorkSpace::ShutdownModeForceNow;
549 if (DMType == OldGDM) {
550 cmd.
append(shutdownMode == KWorkSpace::ShutdownModeForceNow ?
"SET_LOGOUT_ACTION " :
"SET_SAFE_LOGOUT_ACTION ");
551 cmd.
append(shutdownType == KWorkSpace::ShutdownTypeReboot ?
"REBOOT\n" :
"HALT\n");
554 cmd.
append(shutdownType == KWorkSpace::ShutdownTypeReboot ?
"reboot\t" :
"halt\t");
557 cmd.
append(shutdownMode == KWorkSpace::ShutdownModeInteractive ?
"ask\n"
558 : shutdownMode == KWorkSpace::ShutdownModeForceNow ?
"forcenow\n"
559 : shutdownMode == KWorkSpace::ShutdownModeTryNow ?
"trynow\n"
565bool KDisplayManager::bootOptions(
QStringList &opts,
int &defopt,
int ¤t)
567 if (DMType != NewKDM)
571 if (!exec(
"listbootoptions\n", re))
579 defopt = opts[2].toInt(&ok);
582 current = opts[3].toInt(&ok);
588 (*it).
replace(QLatin1String(
"\\s"), QLatin1String(
" "));
594bool KDisplayManager::isSwitchable()
596 if (DMType == NewGDM || DMType == LightDM) {
597 QDBusObjectPath currentSeat;
598 if (getCurrentSeat(
nullptr, ¤tSeat)) {
599 SystemdSeat SDseat(currentSeat);
600 if (SDseat.isValid()) {
601 QVariant prop = SDseat.property(
"CanMultiSession");
613 CKSeat CKseat(currentSeat);
614 if (CKseat.isValid()) {
615 QDBusReply<bool> r = CKseat.call(QStringLiteral(
"CanActivateSessions"));
623 if (DMType == OldKDM)
624 return dpy[0] ==
':';
626 if (DMType == OldGDM)
627 return exec(
"QUERY_VT\n");
631 return exec(
"caps\n", re) && re.
indexOf(
"\tlocal") >= 0;
634int KDisplayManager::numReserve()
636 if (DMType == NewGDM || DMType == OldGDM || DMType == LightDM)
639 if (DMType == OldKDM)
640 return strstr(ctl,
",rsvd") ? 1 : -1;
645 if (!(exec(
"caps\n", re) && (p = re.
indexOf(
"\treserve ")) >= 0))
647 return atoi(re.
data() + p + 9);
650void KDisplayManager::startReserve()
652 if (DMType == NewGDM)
653 GDMFactory().call(QStringLiteral(
"CreateTransientDisplay"));
654 else if (DMType == OldGDM)
655 exec(
"FLEXI_XSERVER\n");
656 else if (DMType == LightDM) {
658 lightDM.
call(QStringLiteral(
"SwitchToGreeter"));
663bool KDisplayManager::localSessions(SessList &list)
665 if (DMType == OldKDM)
668 if (DMType == NewGDM || DMType == LightDM) {
669 QDBusObjectPath currentSession, currentSeat;
670 if (getCurrentSeat(¤tSession, ¤tSeat)) {
674 const auto sessionsForSeat = getSessionsForSeat(currentSeat);
675 for (
const QDBusObjectPath &sp : sessionsForSeat) {
676 SystemdSession lsess(sp);
677 if (lsess.isValid()) {
679 lsess.getSessionLocation(se);
680 if ((lsess.property(
"Class").toString() != QLatin1String(
"greeter"))
681 && (lsess.property(
"State").toString() == QLatin1String(
"online")
682 || lsess.property(
"State").toString() == QLatin1String(
"active"))) {
683 NumberedDBusObjectPath numberedPath = lsess.getUser();
684 se.display = lsess.property(
"Display").toString();
685 se.vt = lsess.property(
"VTNr").toInt();
686 se.user = KUser(K_UID(numberedPath.num)).loginName();
692 se.session = QStringLiteral(
"<unknown>");
694 se.self = lsess.property(
"Id").toString() == qEnvironmentVariable(
"XDG_SESSION_ID");
695 se.tty = !lsess.property(
"TTY").toString().isEmpty();
703 const auto sessionsForSeat = getSessionsForSeat(currentSeat);
704 for (
const QDBusObjectPath &sp : sessionsForSeat) {
706 if (lsess.isValid()) {
708 lsess.getSessionLocation(se);
711 QDBusReply<QString> r = lsess.call(QStringLiteral(
"GetSessionType"));
712 if (r.value() != QLatin1String(
"LoginWindow")) {
713 QDBusReply<unsigned> r2 = lsess.call(QStringLiteral(
"GetUnixUser"));
714 se.user = KUser(K_UID(r2.value())).loginName();
715 se.session = QStringLiteral(
"<unknown>");
717 se.self = (sp == currentSession);
731 if (DMType == OldGDM) {
732 if (!exec(
"CONSOLE_SERVERS\n", re))
736 QStringList ts = (*it).split(QChar(u
','));
740 se.vt = ts[2].toInt();
741 se.session = QStringLiteral(
"<unknown>");
742 se.self = ts[0] == QLatin1String(::getenv(
"DISPLAY"));
747 if (!exec(
"list\talllocal\n", re))
751 QStringList ts = (*it).split(QChar(u
','));
754 se.vt = QStringView(ts[1]).mid(2).toInt();
757 se.self = (ts[4].
indexOf(u
'*') >= 0);
758 se.tty = (ts[4].
indexOf(u
't') >= 0);
765void KDisplayManager::sess2Str2(
const SessEnt &se,
QString &user,
QString &loc)
768 user =
i18nc(
"user: …",
"%1: TTY login", se.user);
769 loc = se.vt ? QStringLiteral(
"vt%1").arg(se.vt) : se.display;
773 ?
i18nc(
"… location (TTY or X display)",
"Unused") : se.session == QLatin1String(
"<remote>")
774 ?
i18n(
"X login on remote host") :
i18nc(
"… host",
"X login on %1", se.session)
775 : se.session == QLatin1String(
"<unknown>")
776 ? se.user :
i18nc(
"user: session type",
"%1: %2", se.user, se.session);
778 loc = se.vt ? QStringLiteral(
"%1, vt%2").arg(se.display).arg(se.vt) : se.display;
782QString KDisplayManager::sess2Str(
const SessEnt &se)
786 sess2Str2(se, user, loc);
787 return i18nc(
"session (location)",
"%1 (%2)", user, loc);
790bool KDisplayManager::switchVT(
int vt)
792 if (DMType == NewGDM || DMType == LightDM) {
793 QDBusObjectPath currentSeat;
794 if (getCurrentSeat(
nullptr, ¤tSeat)) {
797 const auto sessionsForSeat = getSessionsForSeat(currentSeat);
798 for (
const QDBusObjectPath &sp : sessionsForSeat) {
799 SystemdSession lsess(sp);
800 if (lsess.isValid()) {
802 lsess.getSessionLocation(se);
804 lsess.call(SYSTEMD_SWITCH_CALL);
812 const auto sessionsForSeat = getSessionsForSeat(currentSeat);
813 for (
const QDBusObjectPath &sp : sessionsForSeat) {
815 if (lsess.isValid()) {
817 lsess.getSessionLocation(se);
821 lsess.call(QStringLiteral(
"Activate"));
831 if (DMType == OldGDM)
832 return exec(QStringLiteral(
"SET_VT %1\n").arg(vt).toLatin1().constData());
834 return exec(QStringLiteral(
"activate\tvt%1\n").arg(vt).toLatin1().constData());
837void KDisplayManager::lockSwitchVT(
int vt)
840 QDBusInterface screensaver(QStringLiteral(
"org.freedesktop.ScreenSaver"), QStringLiteral(
"/ScreenSaver"), QStringLiteral(
"org.freedesktop.ScreenSaver"));
841 screensaver.call(QStringLiteral(
"Lock"));
846void KDisplayManager::GDMAuthenticate()
850 const char *dpy =
nullptr, *dnum, *dne;
855 if (
auto nativeInterface = instance->nativeInterface<QNativeInterface::QX11Application>()) {
856 dpy = DisplayString(nativeInterface->display());
861 dpy = ::getenv(
"DISPLAY");
865 dnum = strchr(dpy,
':') + 1;
866 dne = strchr(dpy,
'.');
867 dnl = dne ? dne - dnum : strlen(dnum);
870 if (!(fp = fopen(XauFileName(),
"r")))
873 while ((xau = XauReadAuth(fp))) {
874 if (xau->family == FamilyLocal && xau->number_length == dnl && !memcmp(xau->number, dnum, dnl) && xau->data_length == 16 && xau->name_length == 18
875 && !memcmp(xau->name,
"MIT-MAGIC-COOKIE-1", 18)) {
876 QString cmd(QStringLiteral(
"AUTH_LOCAL "));
877 for (
int i = 0; i < 16; i++)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QString path(const QString &relativePath)
bool isValid(QStringView ifopt)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QByteArray & append(QByteArrayView data)
const char * constData() const const
qsizetype indexOf(QByteArrayView bv, qsizetype from) const const
void resize(qsizetype newSize, char c)
qsizetype size() const const
QCoreApplication * instance()
QDBusMessage call(QDBus::CallMode mode, const QString &method, Args &&... args)
QString interface() const const
bool isValid() const const
QString path() const const
QString service() const const
QDBusMessage call(const QDBusMessage &message, QDBus::CallMode mode, int timeout) const const
QDBusConnection systemBus()
QDBusInterface(const QString &service, const QString &path, const QString &interface, const QDBusConnection &connection, QObject *parent)
QList< QVariant > arguments() const const
QDBusMessage createMethodCall(const QString &service, const QString &path, const QString &interface, const QString &method)
bool isValid() const const
void append(QList< T > &&value)
const_iterator constBegin() const const
const_iterator constEnd() const const
void replace(qsizetype i, parameter_type value)
qsizetype size() const const
QVariant property(const char *name) const const
QString fromLocal8Bit(QByteArrayView str)
bool isEmpty() const const
QString number(double n, char format, int precision)
QString rightJustified(qsizetype width, QChar fill, bool truncate) const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QByteArray toLocal8Bit() const const
qsizetype indexOf(const QRegularExpression &re, qsizetype from) const const
bool isValid() const const
bool toBool() const const
int toInt(bool *ok) const const
QString toString() const const