File indexing completed on 2024-04-28 16:54:31
0001 /* 0002 SPDX-FileCopyrightText: 2004 Oswald Buddenhagen <ossi@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "kdisplaymanager.h" 0008 0009 #include <kuser.h> 0010 0011 #include <KLocalizedString> 0012 0013 #include <QCoreApplication> 0014 #include <QDBusArgument> 0015 #include <QDBusConnectionInterface> 0016 #include <QDBusInterface> 0017 #include <QDBusMetaType> 0018 #include <QDBusObjectPath> 0019 #include <QDBusReply> 0020 0021 #include "config-X11.h" 0022 #if HAVE_X11 0023 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0024 #include <private/qtx11extras_p.h> 0025 #else 0026 #include <QX11Info> 0027 #endif 0028 0029 #include <X11/Xauth.h> 0030 #include <X11/Xlib.h> 0031 #endif // HAVE_X11 0032 0033 #include <errno.h> 0034 #include <fcntl.h> 0035 #include <stdio.h> 0036 #include <stdlib.h> 0037 #include <sys/socket.h> 0038 #include <sys/types.h> 0039 #include <sys/un.h> 0040 #include <unistd.h> 0041 0042 #define _DBUS_PROPERTIES_IFACE "org.freedesktop.DBus.Properties" 0043 #define _DBUS_PROPERTIES_GET "Get" 0044 0045 #define DBUS_PROPERTIES_IFACE QLatin1String(_DBUS_PROPERTIES_IFACE) 0046 #define DBUS_PROPERTIES_GET QLatin1String(_DBUS_PROPERTIES_GET) 0047 0048 #define _SYSTEMD_SERVICE "org.freedesktop.login1" 0049 #define _SYSTEMD_BASE_PATH "/org/freedesktop/login1" 0050 #define _SYSTEMD_MANAGER_IFACE _SYSTEMD_SERVICE ".Manager" 0051 #define _SYSTEMD_SESSION_BASE_PATH _SYSTEMD_BASE_PATH "/session" 0052 #define _SYSTEMD_SEAT_IFACE _SYSTEMD_SERVICE ".Seat" 0053 #define _SYSTEMD_SEAT_BASE_PATH _SYSTEMD_BASE_PATH "/seat" 0054 #define _SYSTEMD_SESSION_IFACE _SYSTEMD_SERVICE ".Session" 0055 #define _SYSTEMD_USER_PROPERTY "User" 0056 #define _SYSTEMD_SEAT_PROPERTY "Seat" 0057 #define _SYSTEMD_SESSIONS_PROPERTY "Sessions" 0058 #define _SYSTEMD_SWITCH_PROPERTY "Activate" 0059 0060 #define SYSTEMD_SERVICE QLatin1String(_SYSTEMD_SERVICE) 0061 #define SYSTEMD_BASE_PATH QLatin1String(_SYSTEMD_BASE_PATH) 0062 #define SYSTEMD_MANAGER_IFACE QLatin1String(_SYSTEMD_MANAGER_IFACE) 0063 #define SYSTEMD_SESSION_BASE_PATH QLatin1String(_SYSTEMD_SESSION_BASE_PATH) 0064 #define SYSTEMD_SEAT_IFACE QLatin1String(_SYSTEMD_SEAT_IFACE) 0065 #define SYSTEMD_SEAT_BASE_PATH QLatin1String(_SYSTEMD_SEAT_BASE_PATH) 0066 #define SYSTEMD_SESSION_IFACE QLatin1String(_SYSTEMD_SESSION_IFACE) 0067 #define SYSTEMD_USER_PROPERTY QLatin1String(_SYSTEMD_USER_PROPERTY) 0068 #define SYSTEMD_SEAT_PROPERTY QLatin1String(_SYSTEMD_SEAT_PROPERTY) 0069 #define SYSTEMD_SESSIONS_PROPERTY QLatin1String(_SYSTEMD_SESSIONS_PROPERTY) 0070 #define SYSTEMD_SWITCH_CALL QLatin1String(_SYSTEMD_SWITCH_PROPERTY) 0071 0072 struct NamedDBusObjectPath { 0073 QString name; 0074 QDBusObjectPath path; 0075 }; 0076 Q_DECLARE_METATYPE(NamedDBusObjectPath) 0077 Q_DECLARE_METATYPE(QList<NamedDBusObjectPath>) 0078 0079 // Marshall the NamedDBusObjectPath data into a D-Bus argument 0080 QDBusArgument &operator<<(QDBusArgument &argument, const NamedDBusObjectPath &namedPath) 0081 { 0082 argument.beginStructure(); 0083 argument << namedPath.name << namedPath.path; 0084 argument.endStructure(); 0085 return argument; 0086 } 0087 0088 // Retrieve the NamedDBusObjectPath data from the D-Bus argument 0089 const QDBusArgument &operator>>(const QDBusArgument &argument, NamedDBusObjectPath &namedPath) 0090 { 0091 argument.beginStructure(); 0092 argument >> namedPath.name >> namedPath.path; 0093 argument.endStructure(); 0094 return argument; 0095 } 0096 0097 struct NumberedDBusObjectPath { 0098 uint num; 0099 QDBusObjectPath path; 0100 }; 0101 Q_DECLARE_METATYPE(NumberedDBusObjectPath) 0102 0103 // Marshall the NumberedDBusObjectPath data into a D-Bus argument 0104 QDBusArgument &operator<<(QDBusArgument &argument, const NumberedDBusObjectPath &numberedPath) 0105 { 0106 argument.beginStructure(); 0107 argument << numberedPath.num << numberedPath.path; 0108 argument.endStructure(); 0109 return argument; 0110 } 0111 0112 // Retrieve the NumberedDBusObjectPath data from the D-Bus argument 0113 const QDBusArgument &operator>>(const QDBusArgument &argument, NumberedDBusObjectPath &numberedPath) 0114 { 0115 argument.beginStructure(); 0116 argument >> numberedPath.num >> numberedPath.path; 0117 argument.endStructure(); 0118 return argument; 0119 } 0120 0121 class SystemdManager : public QDBusInterface 0122 { 0123 public: 0124 SystemdManager() 0125 : QDBusInterface(SYSTEMD_SERVICE, SYSTEMD_BASE_PATH, SYSTEMD_MANAGER_IFACE, QDBusConnection::systemBus()) 0126 { 0127 } 0128 }; 0129 0130 class SystemdSeat : public QDBusInterface 0131 { 0132 public: 0133 SystemdSeat(const QDBusObjectPath &path) 0134 : QDBusInterface(SYSTEMD_SERVICE, path.path(), SYSTEMD_SEAT_IFACE, QDBusConnection::systemBus()) 0135 { 0136 } 0137 /* HACK to be able to extract a(so) type from QDBus, property doesn't do the trick */ 0138 QList<NamedDBusObjectPath> getSessions() 0139 { 0140 QDBusMessage message = QDBusMessage::createMethodCall(service(), path(), DBUS_PROPERTIES_IFACE, DBUS_PROPERTIES_GET); 0141 message << interface() << SYSTEMD_SESSIONS_PROPERTY; 0142 QDBusMessage reply = QDBusConnection::systemBus().call(message); 0143 0144 QVariantList args = reply.arguments(); 0145 if (!args.isEmpty()) { 0146 QList<NamedDBusObjectPath> namedPathList = 0147 qdbus_cast<QList<NamedDBusObjectPath>>(args.at(0).value<QDBusVariant>().variant().value<QDBusArgument>()); 0148 return namedPathList; 0149 } 0150 return QList<NamedDBusObjectPath>(); 0151 } 0152 }; 0153 0154 class SystemdSession : public QDBusInterface 0155 { 0156 public: 0157 SystemdSession(const QDBusObjectPath &path) 0158 : QDBusInterface(SYSTEMD_SERVICE, path.path(), SYSTEMD_SESSION_IFACE, QDBusConnection::systemBus()) 0159 { 0160 } 0161 /* HACK to be able to extract (so) type from QDBus, property doesn't do the trick */ 0162 NamedDBusObjectPath getSeat() 0163 { 0164 QDBusMessage message = QDBusMessage::createMethodCall(service(), path(), DBUS_PROPERTIES_IFACE, DBUS_PROPERTIES_GET); 0165 message << interface() << SYSTEMD_SEAT_PROPERTY; 0166 QDBusMessage reply = QDBusConnection::systemBus().call(message); 0167 0168 QVariantList args = reply.arguments(); 0169 if (!args.isEmpty()) { 0170 NamedDBusObjectPath namedPath; 0171 args.at(0).value<QDBusVariant>().variant().value<QDBusArgument>() >> namedPath; 0172 return namedPath; 0173 } 0174 return NamedDBusObjectPath(); 0175 } 0176 NumberedDBusObjectPath getUser() 0177 { 0178 QDBusMessage message = QDBusMessage::createMethodCall(service(), path(), DBUS_PROPERTIES_IFACE, DBUS_PROPERTIES_GET); 0179 message << interface() << SYSTEMD_USER_PROPERTY; 0180 QDBusMessage reply = QDBusConnection::systemBus().call(message); 0181 0182 QVariantList args = reply.arguments(); 0183 if (!args.isEmpty()) { 0184 NumberedDBusObjectPath numberedPath; 0185 args.at(0).value<QDBusVariant>().variant().value<QDBusArgument>() >> numberedPath; 0186 return numberedPath; 0187 } 0188 return NumberedDBusObjectPath(); 0189 } 0190 void getSessionLocation(SessEnt &se) 0191 { 0192 se.tty = (property("Type").toString() != QLatin1String("x11")); 0193 se.display = property(se.tty ? "TTY" : "Display").toString(); 0194 se.vt = property("VTNr").toInt(); 0195 } 0196 }; 0197 0198 class CKManager : public QDBusInterface 0199 { 0200 public: 0201 CKManager() 0202 : QDBusInterface(QStringLiteral("org.freedesktop.ConsoleKit"), 0203 QStringLiteral("/org/freedesktop/ConsoleKit/Manager"), 0204 QStringLiteral("org.freedesktop.ConsoleKit.Manager"), 0205 QDBusConnection::systemBus()) 0206 { 0207 } 0208 }; 0209 0210 class CKSeat : public QDBusInterface 0211 { 0212 public: 0213 CKSeat(const QDBusObjectPath &path) 0214 : QDBusInterface(QStringLiteral("org.freedesktop.ConsoleKit"), 0215 path.path(), 0216 QStringLiteral("org.freedesktop.ConsoleKit.Seat"), 0217 QDBusConnection::systemBus()) 0218 { 0219 } 0220 }; 0221 0222 class CKSession : public QDBusInterface 0223 { 0224 public: 0225 CKSession(const QDBusObjectPath &path) 0226 : QDBusInterface(QStringLiteral("org.freedesktop.ConsoleKit"), 0227 path.path(), 0228 QStringLiteral("org.freedesktop.ConsoleKit.Session"), 0229 QDBusConnection::systemBus()) 0230 { 0231 } 0232 void getSessionLocation(SessEnt &se) 0233 { 0234 QString tty; 0235 QDBusReply<QString> r = call(QStringLiteral("GetX11Display")); 0236 if (r.isValid() && !r.value().isEmpty()) { 0237 QDBusReply<QString> r2 = call(QStringLiteral("GetX11DisplayDevice")); 0238 tty = r2.value(); 0239 se.display = r.value(); 0240 se.tty = false; 0241 } else { 0242 QDBusReply<QString> r2 = call(QStringLiteral("GetDisplayDevice")); 0243 tty = r2.value(); 0244 se.display = tty; 0245 se.tty = true; 0246 } 0247 se.vt = QStringView(tty).mid(strlen("/dev/tty")).toInt(); 0248 } 0249 }; 0250 0251 class GDMFactory : public QDBusInterface 0252 { 0253 public: 0254 GDMFactory() 0255 : QDBusInterface(QStringLiteral("org.gnome.DisplayManager"), 0256 QStringLiteral("/org/gnome/DisplayManager/LocalDisplayFactory"), 0257 QStringLiteral("org.gnome.DisplayManager.LocalDisplayFactory"), 0258 QDBusConnection::systemBus()) 0259 { 0260 } 0261 }; 0262 0263 class LightDMDBus : public QDBusInterface 0264 { 0265 public: 0266 LightDMDBus() 0267 : QDBusInterface(QStringLiteral("org.freedesktop.DisplayManager"), 0268 qgetenv("XDG_SEAT_PATH"), 0269 QStringLiteral("org.freedesktop.DisplayManager.Seat"), 0270 QDBusConnection::systemBus()) 0271 { 0272 } 0273 }; 0274 0275 static enum { 0276 Dunno, 0277 NoDM, 0278 NewKDM, 0279 OldKDM, 0280 NewGDM, 0281 OldGDM, 0282 LightDM, 0283 } DMType = Dunno; 0284 static const char *ctl, *dpy; 0285 0286 class KDisplayManager::Private 0287 { 0288 public: 0289 Private() 0290 : fd(-1) 0291 { 0292 } 0293 ~Private() 0294 { 0295 if (fd >= 0) 0296 close(fd); 0297 } 0298 0299 int fd; 0300 }; 0301 0302 KDisplayManager::KDisplayManager() 0303 : d(new Private) 0304 { 0305 const char *ptr; 0306 struct sockaddr_un sa; 0307 0308 qDBusRegisterMetaType<NamedDBusObjectPath>(); 0309 qDBusRegisterMetaType<QList<NamedDBusObjectPath>>(); 0310 qDBusRegisterMetaType<NumberedDBusObjectPath>(); 0311 0312 if (DMType == Dunno) { 0313 dpy = ::getenv("DISPLAY"); 0314 if (dpy && (ctl = ::getenv("DM_CONTROL"))) 0315 DMType = NewKDM; 0316 else if (dpy && (ctl = ::getenv("XDM_MANAGED")) && ctl[0] == '/') 0317 DMType = OldKDM; 0318 else if (::getenv("XDG_SEAT_PATH") && LightDMDBus().isValid()) 0319 DMType = LightDM; 0320 else if (::getenv("GDMSESSION")) 0321 DMType = GDMFactory().isValid() ? NewGDM : OldGDM; 0322 else 0323 DMType = NoDM; 0324 } 0325 switch (DMType) { 0326 default: 0327 return; 0328 case NewKDM: 0329 case OldGDM: 0330 if ((d->fd = ::socket(PF_UNIX, SOCK_STREAM, 0)) < 0) 0331 return; 0332 sa.sun_family = AF_UNIX; 0333 if (DMType == OldGDM) { 0334 strcpy(sa.sun_path, "/var/run/gdm_socket"); 0335 if (::connect(d->fd, (struct sockaddr *)&sa, sizeof(sa))) { 0336 strcpy(sa.sun_path, "/tmp/.gdm_socket"); 0337 if (::connect(d->fd, (struct sockaddr *)&sa, sizeof(sa))) { 0338 ::close(d->fd); 0339 d->fd = -1; 0340 break; 0341 } 0342 } 0343 GDMAuthenticate(); 0344 } else { 0345 if ((ptr = strchr(dpy, ':'))) 0346 ptr = strchr(ptr, '.'); 0347 snprintf(sa.sun_path, sizeof(sa.sun_path), "%s/dmctl-%.*s/socket", ctl, ptr ? int(ptr - dpy) : 512, dpy); 0348 if (::connect(d->fd, (struct sockaddr *)&sa, sizeof(sa))) { 0349 ::close(d->fd); 0350 d->fd = -1; 0351 } 0352 } 0353 break; 0354 case OldKDM: { 0355 QString tf(ctl); 0356 tf.truncate(tf.indexOf(',')); 0357 d->fd = ::open(tf.toLatin1(), O_WRONLY); 0358 } break; 0359 } 0360 } 0361 0362 KDisplayManager::~KDisplayManager() 0363 { 0364 delete d; 0365 } 0366 0367 bool KDisplayManager::exec(const char *cmd) 0368 { 0369 QByteArray buf; 0370 0371 return exec(cmd, buf); 0372 } 0373 0374 /** 0375 * Execute a KDM/GDM remote control command. 0376 * @param cmd the command to execute. FIXME: undocumented yet. 0377 * @param buf the result buffer. 0378 * @return result: 0379 * @li If true, the command was successfully executed. 0380 * @p ret might contain additional results. 0381 * @li If false and @p ret is empty, a communication error occurred 0382 * (most probably KDM is not running). 0383 * @li If false and @p ret is non-empty, it contains the error message 0384 * from KDM. 0385 */ 0386 bool KDisplayManager::exec(const char *cmd, QByteArray &buf) 0387 { 0388 bool ret = false; 0389 int tl; 0390 int len = 0; 0391 0392 if (d->fd < 0) 0393 goto busted; 0394 0395 tl = strlen(cmd); 0396 if (::write(d->fd, cmd, tl) != tl) { 0397 bust: 0398 ::close(d->fd); 0399 d->fd = -1; 0400 busted: 0401 buf.resize(0); 0402 return false; 0403 } 0404 if (DMType == OldKDM) { 0405 buf.resize(0); 0406 return true; 0407 } 0408 for (;;) { 0409 if (buf.size() < 128) 0410 buf.resize(128); 0411 else if (buf.size() < len * 2) 0412 buf.resize(len * 2); 0413 if ((tl = ::read(d->fd, buf.data() + len, buf.size() - len)) <= 0) { 0414 if (tl < 0 && errno == EINTR) 0415 continue; 0416 goto bust; 0417 } 0418 len += tl; 0419 if (buf[len - 1] == '\n') { 0420 buf[len - 1] = 0; 0421 if (len > 2 && (buf[0] == 'o' || buf[0] == 'O') && (buf[1] == 'k' || buf[1] == 'K') && buf[2] <= ' ') 0422 ret = true; 0423 break; 0424 } 0425 } 0426 return ret; 0427 } 0428 0429 static bool getCurrentSeat(QDBusObjectPath *currentSession, QDBusObjectPath *currentSeat) 0430 { 0431 SystemdManager man; 0432 if (man.isValid()) { 0433 *currentSeat = QDBusObjectPath(_SYSTEMD_SEAT_BASE_PATH "/auto"); 0434 SystemdSeat seat(*currentSeat); 0435 if (seat.property("Id").isValid()) { // query an arbitrary property to confirm the path is valid 0436 return true; 0437 } 0438 0439 // auto is newer and may not exist on all platforms, fallback to GetSessionByPID if the above failed 0440 0441 QDBusReply<QDBusObjectPath> r = man.call(QStringLiteral("GetSessionByPID"), (uint)QCoreApplication::applicationPid()); 0442 if (r.isValid()) { 0443 SystemdSession sess(r.value()); 0444 if (sess.isValid()) { 0445 NamedDBusObjectPath namedPath = sess.getSeat(); 0446 *currentSeat = namedPath.path; 0447 return true; 0448 } 0449 } 0450 } else { 0451 CKManager man; 0452 QDBusReply<QDBusObjectPath> r = man.call(QStringLiteral("GetCurrentSession")); 0453 if (r.isValid()) { 0454 CKSession sess(r.value()); 0455 if (sess.isValid()) { 0456 QDBusReply<QDBusObjectPath> r2 = sess.call(QStringLiteral("GetSeatId")); 0457 if (r2.isValid()) { 0458 if (currentSession) 0459 *currentSession = r.value(); 0460 *currentSeat = r2.value(); 0461 return true; 0462 } 0463 } 0464 } 0465 } 0466 return false; 0467 } 0468 0469 static QList<QDBusObjectPath> getSessionsForSeat(const QDBusObjectPath &path) 0470 { 0471 if (path.path().startsWith(SYSTEMD_BASE_PATH)) { // systemd path incoming 0472 SystemdSeat seat(path); 0473 if (seat.isValid()) { 0474 const QList<NamedDBusObjectPath> r = seat.getSessions(); 0475 QList<QDBusObjectPath> result; 0476 for (const NamedDBusObjectPath &namedPath : r) 0477 result.append(namedPath.path); 0478 // This pretty much can't contain any other than local sessions as the seat is retrieved from the current session 0479 return result; 0480 } 0481 } else if (path.path().startsWith(QLatin1String("/org/freedesktop/ConsoleKit"))) { 0482 CKSeat seat(path); 0483 if (seat.isValid()) { 0484 QDBusReply<QList<QDBusObjectPath>> r = seat.call(QStringLiteral("GetSessions")); 0485 if (r.isValid()) { 0486 // This will contain only local sessions: 0487 // - this is only ever called when isSwitchable() is true => local seat 0488 // - remote logins into the machine are assigned to other seats 0489 return r.value(); 0490 } 0491 } 0492 } 0493 return QList<QDBusObjectPath>(); 0494 } 0495 0496 #ifndef KDM_NO_SHUTDOWN 0497 bool KDisplayManager::canShutdown() 0498 { 0499 if (DMType == NewGDM || DMType == NoDM || DMType == LightDM) { 0500 QDBusReply<QString> canPowerOff = SystemdManager().call(QStringLiteral("CanPowerOff")); 0501 if (canPowerOff.isValid()) 0502 return canPowerOff.value() != QLatin1String("no"); 0503 QDBusReply<bool> canStop = CKManager().call(QStringLiteral("CanStop")); 0504 if (canStop.isValid()) 0505 return canStop.value(); 0506 return false; 0507 } 0508 0509 if (DMType == OldKDM) 0510 return strstr(ctl, ",maysd") != nullptr; 0511 0512 QByteArray re; 0513 0514 if (DMType == OldGDM) 0515 return exec("QUERY_LOGOUT_ACTION\n", re) && re.indexOf("HALT") >= 0; 0516 0517 return exec("caps\n", re) && re.indexOf("\tshutdown") >= 0; 0518 } 0519 0520 void KDisplayManager::shutdown(KWorkSpace::ShutdownType shutdownType, 0521 KWorkSpace::ShutdownMode shutdownMode, /* NOT Default */ 0522 const QString &bootOption) 0523 { 0524 if (shutdownType == KWorkSpace::ShutdownTypeNone || shutdownType == KWorkSpace::ShutdownTypeLogout) 0525 return; 0526 0527 bool cap_ask; 0528 if (DMType == NewKDM) { 0529 QByteArray re; 0530 cap_ask = exec("caps\n", re) && re.indexOf("\tshutdown ask") >= 0; 0531 } else { 0532 if (!bootOption.isEmpty()) 0533 return; 0534 0535 if (DMType == NewGDM || DMType == NoDM || DMType == LightDM) { 0536 // systemd supports only 2 modes: 0537 // * interactive = true: brings up a PolicyKit prompt if other sessions are active 0538 // * interactive = false: rejects the shutdown if other sessions are active 0539 // There are no schedule or force modes. 0540 // We try to map our 4 shutdown modes in the sanest way. 0541 bool interactive = (shutdownMode == KWorkSpace::ShutdownModeInteractive || shutdownMode == KWorkSpace::ShutdownModeForceNow); 0542 QDBusReply<QString> check = 0543 SystemdManager().call(QLatin1String(shutdownType == KWorkSpace::ShutdownTypeReboot ? "Reboot" : "PowerOff"), interactive); 0544 if (!check.isValid()) { 0545 // FIXME: entirely ignoring shutdownMode 0546 CKManager().call(QLatin1String(shutdownType == KWorkSpace::ShutdownTypeReboot ? "Restart" : "Stop")); 0547 // if even CKManager call fails, there is nothing more to be done 0548 } 0549 return; 0550 } 0551 0552 cap_ask = false; 0553 } 0554 if (!cap_ask && shutdownMode == KWorkSpace::ShutdownModeInteractive) 0555 shutdownMode = KWorkSpace::ShutdownModeForceNow; 0556 0557 QByteArray cmd; 0558 if (DMType == OldGDM) { 0559 cmd.append(shutdownMode == KWorkSpace::ShutdownModeForceNow ? "SET_LOGOUT_ACTION " : "SET_SAFE_LOGOUT_ACTION "); 0560 cmd.append(shutdownType == KWorkSpace::ShutdownTypeReboot ? "REBOOT\n" : "HALT\n"); 0561 } else { 0562 cmd.append("shutdown\t"); 0563 cmd.append(shutdownType == KWorkSpace::ShutdownTypeReboot ? "reboot\t" : "halt\t"); 0564 if (!bootOption.isEmpty()) 0565 cmd.append("=").append(bootOption.toLocal8Bit()).append("\t"); 0566 cmd.append(shutdownMode == KWorkSpace::ShutdownModeInteractive ? "ask\n" 0567 : shutdownMode == KWorkSpace::ShutdownModeForceNow ? "forcenow\n" 0568 : shutdownMode == KWorkSpace::ShutdownModeTryNow ? "trynow\n" 0569 : "schedule\n"); 0570 } 0571 exec(cmd.data()); 0572 } 0573 0574 bool KDisplayManager::bootOptions(QStringList &opts, int &defopt, int ¤t) 0575 { 0576 if (DMType != NewKDM) 0577 return false; 0578 0579 QByteArray re; 0580 if (!exec("listbootoptions\n", re)) 0581 return false; 0582 0583 opts = QString::fromLocal8Bit(re.data()).split('\t', Qt::SkipEmptyParts); 0584 if (opts.size() < 4) 0585 return false; 0586 0587 bool ok; 0588 defopt = opts[2].toInt(&ok); 0589 if (!ok) 0590 return false; 0591 current = opts[3].toInt(&ok); 0592 if (!ok) 0593 return false; 0594 0595 opts = opts[1].split(' ', Qt::SkipEmptyParts); 0596 for (QStringList::Iterator it = opts.begin(); it != opts.end(); ++it) 0597 (*it).replace(QLatin1String("\\s"), QLatin1String(" ")); 0598 0599 return true; 0600 } 0601 #endif // KDM_NO_SHUTDOWN 0602 0603 bool KDisplayManager::isSwitchable() 0604 { 0605 if (DMType == NewGDM || DMType == LightDM) { 0606 QDBusObjectPath currentSeat; 0607 if (getCurrentSeat(nullptr, ¤tSeat)) { 0608 SystemdSeat SDseat(currentSeat); 0609 if (SDseat.isValid()) { 0610 QVariant prop = SDseat.property("CanMultiSession"); 0611 if (prop.isValid()) 0612 return prop.toBool(); 0613 else { 0614 // Newer systemd versions (since 246) don't expose "CanMultiSession" anymore. 0615 // It's hidden and always true. 0616 // See https://github.com/systemd/systemd/commit/8f8cc84ba4612e74cd1e26898c6816e6e60fc4e9 0617 // and https://github.com/systemd/systemd/commit/c2b178d3cacad52eadc30ecc349160bc02d32a9c 0618 // So assume that it's supported if the property is invalid. 0619 return true; 0620 } 0621 } 0622 CKSeat CKseat(currentSeat); 0623 if (CKseat.isValid()) { 0624 QDBusReply<bool> r = CKseat.call(QStringLiteral("CanActivateSessions")); 0625 if (r.isValid()) 0626 return r.value(); 0627 } 0628 } 0629 return false; 0630 } 0631 0632 if (DMType == OldKDM) 0633 return dpy[0] == ':'; 0634 0635 if (DMType == OldGDM) 0636 return exec("QUERY_VT\n"); 0637 0638 QByteArray re; 0639 0640 return exec("caps\n", re) && re.indexOf("\tlocal") >= 0; 0641 } 0642 0643 int KDisplayManager::numReserve() 0644 { 0645 if (DMType == NewGDM || DMType == OldGDM || DMType == LightDM) 0646 return 1; /* Bleh */ 0647 0648 if (DMType == OldKDM) 0649 return strstr(ctl, ",rsvd") ? 1 : -1; 0650 0651 QByteArray re; 0652 int p; 0653 0654 if (!(exec("caps\n", re) && (p = re.indexOf("\treserve ")) >= 0)) 0655 return -1; 0656 return atoi(re.data() + p + 9); 0657 } 0658 0659 void KDisplayManager::startReserve() 0660 { 0661 if (DMType == NewGDM) 0662 GDMFactory().call(QStringLiteral("CreateTransientDisplay")); 0663 else if (DMType == OldGDM) 0664 exec("FLEXI_XSERVER\n"); 0665 else if (DMType == LightDM) { 0666 LightDMDBus lightDM; 0667 lightDM.call(QStringLiteral("SwitchToGreeter")); 0668 } else 0669 exec("reserve\n"); 0670 } 0671 0672 bool KDisplayManager::localSessions(SessList &list) 0673 { 0674 if (DMType == OldKDM) 0675 return false; 0676 0677 if (DMType == NewGDM || DMType == LightDM) { 0678 QDBusObjectPath currentSession, currentSeat; 0679 if (getCurrentSeat(¤tSession, ¤tSeat)) { 0680 // we'll divide the code in two branches to reduce the overhead of calls to non-existent services 0681 // systemd part // preferred 0682 if (QDBusConnection::systemBus().interface()->isServiceRegistered(SYSTEMD_SERVICE)) { 0683 const auto sessionsForSeat = getSessionsForSeat(currentSeat); 0684 for (const QDBusObjectPath &sp : sessionsForSeat) { 0685 SystemdSession lsess(sp); 0686 if (lsess.isValid()) { 0687 SessEnt se; 0688 lsess.getSessionLocation(se); 0689 if ((lsess.property("Class").toString() != QLatin1String("greeter")) 0690 && (lsess.property("State").toString() == QLatin1String("online") 0691 || lsess.property("State").toString() == QLatin1String("active"))) { 0692 NumberedDBusObjectPath numberedPath = lsess.getUser(); 0693 se.display = lsess.property("Display").toString(); 0694 se.vt = lsess.property("VTNr").toInt(); 0695 se.user = KUser(K_UID(numberedPath.num)).loginName(); 0696 /* TODO: 0697 * regarding the session name in this, it IS possible to find it out - logind tracks the session leader PID 0698 * the problem is finding out the name of the process, I could come only with reading /proc/PID/comm which 0699 * doesn't seem exactly... right to me --mbriza 0700 */ 0701 se.session = QStringLiteral("<unknown>"); 0702 0703 se.self = lsess.property("Id").toString() == qgetenv("XDG_SESSION_ID"); 0704 se.tty = !lsess.property("TTY").toString().isEmpty(); 0705 } 0706 list.append(se); 0707 } 0708 } 0709 } 0710 // ConsoleKit part 0711 else if (QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.ConsoleKit"))) { 0712 const auto sessionsForSeat = getSessionsForSeat(currentSeat); 0713 for (const QDBusObjectPath &sp : sessionsForSeat) { 0714 CKSession lsess(sp); 0715 if (lsess.isValid()) { 0716 SessEnt se; 0717 lsess.getSessionLocation(se); 0718 // "Warning: we haven't yet defined the allowed values for this property. 0719 // It is probably best to avoid this until we do." 0720 QDBusReply<QString> r = lsess.call(QStringLiteral("GetSessionType")); 0721 if (r.value() != QLatin1String("LoginWindow")) { 0722 QDBusReply<unsigned> r2 = lsess.call(QStringLiteral("GetUnixUser")); 0723 se.user = KUser(K_UID(r2.value())).loginName(); 0724 se.session = QStringLiteral("<unknown>"); 0725 } 0726 se.self = (sp == currentSession); 0727 list.append(se); 0728 } 0729 } 0730 } else { 0731 return false; 0732 } 0733 return true; 0734 } 0735 return false; 0736 } 0737 0738 QByteArray re; 0739 0740 if (DMType == OldGDM) { 0741 if (!exec("CONSOLE_SERVERS\n", re)) 0742 return false; 0743 const QStringList sess = QString(re.data() + 3).split(QChar(';'), Qt::SkipEmptyParts); 0744 for (QStringList::ConstIterator it = sess.constBegin(); it != sess.constEnd(); ++it) { 0745 QStringList ts = (*it).split(QChar(',')); 0746 SessEnt se; 0747 se.display = ts[0]; 0748 se.user = ts[1]; 0749 se.vt = ts[2].toInt(); 0750 se.session = QStringLiteral("<unknown>"); 0751 se.self = ts[0] == ::getenv("DISPLAY"); /* Bleh */ 0752 se.tty = false; 0753 list.append(se); 0754 } 0755 } else { 0756 if (!exec("list\talllocal\n", re)) 0757 return false; 0758 const QStringList sess = QString(re.data() + 3).split(QChar('\t'), Qt::SkipEmptyParts); 0759 for (QStringList::ConstIterator it = sess.constBegin(); it != sess.constEnd(); ++it) { 0760 QStringList ts = (*it).split(QChar(',')); 0761 SessEnt se; 0762 se.display = ts[0]; 0763 se.vt = QStringView(ts[1]).mid(2).toInt(); 0764 se.user = ts[2]; 0765 se.session = ts[3]; 0766 se.self = (ts[4].indexOf('*') >= 0); 0767 se.tty = (ts[4].indexOf('t') >= 0); 0768 list.append(se); 0769 } 0770 } 0771 return true; 0772 } 0773 0774 void KDisplayManager::sess2Str2(const SessEnt &se, QString &user, QString &loc) 0775 { 0776 if (se.tty) { 0777 user = i18nc("user: …", "%1: TTY login", se.user); 0778 loc = se.vt ? QStringLiteral("vt%1").arg(se.vt) : se.display; 0779 } else { 0780 // clang-format off 0781 user = se.user.isEmpty() ? se.session.isEmpty() 0782 ? i18nc("… location (TTY or X display)", "Unused") : se.session == QLatin1String("<remote>") 0783 ? i18n("X login on remote host") : i18nc("… host", "X login on %1", se.session) 0784 : se.session == QLatin1String("<unknown>") 0785 ? se.user : i18nc("user: session type", "%1: %2", se.user, se.session); 0786 // clang-format on 0787 loc = se.vt ? QStringLiteral("%1, vt%2").arg(se.display).arg(se.vt) : se.display; 0788 } 0789 } 0790 0791 QString KDisplayManager::sess2Str(const SessEnt &se) 0792 { 0793 QString user, loc; 0794 0795 sess2Str2(se, user, loc); 0796 return i18nc("session (location)", "%1 (%2)", user, loc); 0797 } 0798 0799 bool KDisplayManager::switchVT(int vt) 0800 { 0801 if (DMType == NewGDM || DMType == LightDM) { 0802 QDBusObjectPath currentSeat; 0803 if (getCurrentSeat(nullptr, ¤tSeat)) { 0804 // systemd part // preferred 0805 if (QDBusConnection::systemBus().interface()->isServiceRegistered(SYSTEMD_SERVICE)) { 0806 const auto sessionsForSeat = getSessionsForSeat(currentSeat); 0807 for (const QDBusObjectPath &sp : sessionsForSeat) { 0808 SystemdSession lsess(sp); 0809 if (lsess.isValid()) { 0810 SessEnt se; 0811 lsess.getSessionLocation(se); 0812 if (se.vt == vt) { 0813 lsess.call(SYSTEMD_SWITCH_CALL); 0814 return true; 0815 } 0816 } 0817 } 0818 } 0819 // ConsoleKit part 0820 else if (QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.ConsoleKit"))) { 0821 const auto sessionsForSeat = getSessionsForSeat(currentSeat); 0822 for (const QDBusObjectPath &sp : sessionsForSeat) { 0823 CKSession lsess(sp); 0824 if (lsess.isValid()) { 0825 SessEnt se; 0826 lsess.getSessionLocation(se); 0827 if (se.vt == vt) { 0828 if (se.tty) // ConsoleKit simply ignores these 0829 return false; 0830 lsess.call(QStringLiteral("Activate")); 0831 return true; 0832 } 0833 } 0834 } 0835 } 0836 } 0837 return false; 0838 } 0839 0840 if (DMType == OldGDM) 0841 return exec(QStringLiteral("SET_VT %1\n").arg(vt).toLatin1()); 0842 0843 return exec(QStringLiteral("activate\tvt%1\n").arg(vt).toLatin1()); 0844 } 0845 0846 void KDisplayManager::lockSwitchVT(int vt) 0847 { 0848 // Lock first, otherwise the lock won't be able to kick in until the session is re-activated. 0849 QDBusInterface screensaver(QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("/ScreenSaver"), QStringLiteral("org.freedesktop.ScreenSaver")); 0850 screensaver.call(QStringLiteral("Lock")); 0851 0852 switchVT(vt); 0853 } 0854 0855 void KDisplayManager::GDMAuthenticate() 0856 { 0857 #if HAVE_X11 0858 FILE *fp; 0859 const char *dpy, *dnum, *dne; 0860 int dnl; 0861 Xauth *xau; 0862 0863 dpy = DisplayString(QX11Info::display()); 0864 if (!dpy) { 0865 dpy = ::getenv("DISPLAY"); 0866 if (!dpy) 0867 return; 0868 } 0869 dnum = strchr(dpy, ':') + 1; 0870 dne = strchr(dpy, '.'); 0871 dnl = dne ? dne - dnum : strlen(dnum); 0872 0873 /* XXX should do locking */ 0874 if (!(fp = fopen(XauFileName(), "r"))) 0875 return; 0876 0877 while ((xau = XauReadAuth(fp))) { 0878 if (xau->family == FamilyLocal && xau->number_length == dnl && !memcmp(xau->number, dnum, dnl) && xau->data_length == 16 && xau->name_length == 18 0879 && !memcmp(xau->name, "MIT-MAGIC-COOKIE-1", 18)) { 0880 QString cmd(QStringLiteral("AUTH_LOCAL ")); 0881 for (int i = 0; i < 16; i++) 0882 cmd += QString::number((uchar)xau->data[i], 16).rightJustified(2, '0'); 0883 cmd += '\n'; 0884 if (exec(cmd.toLatin1())) { 0885 XauDisposeAuth(xau); 0886 break; 0887 } 0888 } 0889 XauDisposeAuth(xau); 0890 } 0891 0892 fclose(fp); 0893 #endif // HAVE_X11 0894 }