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