File indexing completed on 2024-11-10 04:56:40
0001 /* 0002 SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "session_consolekit.h" 0008 #include "utils/common.h" 0009 0010 #include <QCoreApplication> 0011 #include <QDBusConnection> 0012 #include <QDBusConnectionInterface> 0013 #include <QDBusInterface> 0014 #include <QDBusMessage> 0015 #include <QDBusMetaType> 0016 #include <QDBusObjectPath> 0017 #include <QDBusPendingCall> 0018 #include <QDBusUnixFileDescriptor> 0019 0020 #include <fcntl.h> 0021 #include <sys/stat.h> 0022 #include <unistd.h> 0023 0024 #if __has_include(<sys/sysmacros.h>) 0025 #include <sys/sysmacros.h> 0026 #endif 0027 0028 // Note that ConsoleKit's session api is not fully compatible with logind's session api. 0029 0030 struct DBusConsoleKitSeat 0031 { 0032 QString id; 0033 QDBusObjectPath path; 0034 }; 0035 0036 QDBusArgument &operator<<(QDBusArgument &argument, const DBusConsoleKitSeat &seat) 0037 { 0038 argument.beginStructure(); 0039 argument << seat.id << seat.path; 0040 argument.endStructure(); 0041 return argument; 0042 } 0043 0044 const QDBusArgument &operator>>(const QDBusArgument &argument, DBusConsoleKitSeat &seat) 0045 { 0046 argument.beginStructure(); 0047 argument >> seat.id >> seat.path; 0048 argument.endStructure(); 0049 return argument; 0050 } 0051 0052 Q_DECLARE_METATYPE(DBusConsoleKitSeat) 0053 0054 namespace KWin 0055 { 0056 0057 static const QString s_serviceName = QStringLiteral("org.freedesktop.ConsoleKit"); 0058 static const QString s_propertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties"); 0059 static const QString s_sessionInterface = QStringLiteral("org.freedesktop.ConsoleKit.Session"); 0060 static const QString s_seatInterface = QStringLiteral("org.freedesktop.ConsoleKit.Seat"); 0061 static const QString s_managerInterface = QStringLiteral("org.freedesktop.ConsoleKit.Manager"); 0062 static const QString s_managerPath = QStringLiteral("/org/freedesktop/ConsoleKit/Manager"); 0063 0064 static QString findProcessSessionPath() 0065 { 0066 QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, s_managerPath, 0067 s_managerInterface, 0068 QStringLiteral("GetSessionByPID")); 0069 message.setArguments({uint32_t(QCoreApplication::applicationPid())}); 0070 0071 const QDBusMessage reply = QDBusConnection::systemBus().call(message); 0072 if (reply.type() == QDBusMessage::ErrorMessage) { 0073 return QString(); 0074 } 0075 0076 return reply.arguments().constFirst().value<QDBusObjectPath>().path(); 0077 } 0078 0079 static bool takeControl(const QString &sessionPath) 0080 { 0081 QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, sessionPath, 0082 s_sessionInterface, 0083 QStringLiteral("TakeControl")); 0084 message.setArguments({false}); 0085 0086 const QDBusMessage reply = QDBusConnection::systemBus().call(message); 0087 0088 return reply.type() != QDBusMessage::ErrorMessage; 0089 } 0090 0091 static void releaseControl(const QString &sessionPath) 0092 { 0093 const QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, sessionPath, 0094 s_sessionInterface, 0095 QStringLiteral("ReleaseControl")); 0096 0097 QDBusConnection::systemBus().asyncCall(message); 0098 } 0099 0100 static bool activate(const QString &sessionPath) 0101 { 0102 const QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, sessionPath, 0103 s_sessionInterface, 0104 QStringLiteral("Activate")); 0105 0106 const QDBusMessage reply = QDBusConnection::systemBus().call(message); 0107 0108 return reply.type() != QDBusMessage::ErrorMessage; 0109 } 0110 0111 std::unique_ptr<ConsoleKitSession> ConsoleKitSession::create() 0112 { 0113 if (!QDBusConnection::systemBus().interface()->isServiceRegistered(s_serviceName)) { 0114 return nullptr; 0115 } 0116 0117 const QString sessionPath = findProcessSessionPath(); 0118 if (sessionPath.isEmpty()) { 0119 qCWarning(KWIN_CORE) << "Could not determine the active graphical session"; 0120 return nullptr; 0121 } 0122 0123 if (!activate(sessionPath)) { 0124 qCWarning(KWIN_CORE, "Failed to activate %s session. Maybe another compositor is running?", 0125 qPrintable(sessionPath)); 0126 return nullptr; 0127 } 0128 0129 if (!takeControl(sessionPath)) { 0130 qCWarning(KWIN_CORE, "Failed to take control of %s session. Maybe another compositor is running?", 0131 qPrintable(sessionPath)); 0132 return nullptr; 0133 } 0134 0135 std::unique_ptr<ConsoleKitSession> session{new ConsoleKitSession(sessionPath)}; 0136 if (session->initialize()) { 0137 return session; 0138 } else { 0139 return nullptr; 0140 } 0141 } 0142 0143 bool ConsoleKitSession::isActive() const 0144 { 0145 return m_isActive; 0146 } 0147 0148 ConsoleKitSession::Capabilities ConsoleKitSession::capabilities() const 0149 { 0150 return Capability::SwitchTerminal; 0151 } 0152 0153 QString ConsoleKitSession::seat() const 0154 { 0155 return m_seatId; 0156 } 0157 0158 uint ConsoleKitSession::terminal() const 0159 { 0160 return m_terminal; 0161 } 0162 0163 int ConsoleKitSession::openRestricted(const QString &fileName) 0164 { 0165 struct stat st; 0166 if (stat(fileName.toUtf8(), &st) < 0) { 0167 return -1; 0168 } 0169 0170 QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, 0171 s_sessionInterface, 0172 QStringLiteral("TakeDevice")); 0173 // major() and minor() macros return ints on FreeBSD instead of uints. 0174 message.setArguments({uint(major(st.st_rdev)), uint(minor(st.st_rdev))}); 0175 0176 const QDBusMessage reply = QDBusConnection::systemBus().call(message); 0177 if (reply.type() == QDBusMessage::ErrorMessage) { 0178 qCDebug(KWIN_CORE, "Failed to open %s device (%s)", 0179 qPrintable(fileName), qPrintable(reply.errorMessage())); 0180 return -1; 0181 } 0182 0183 const QDBusUnixFileDescriptor descriptor = reply.arguments().constFirst().value<QDBusUnixFileDescriptor>(); 0184 if (!descriptor.isValid()) { 0185 return -1; 0186 } 0187 0188 return fcntl(descriptor.fileDescriptor(), F_DUPFD_CLOEXEC, 0); 0189 } 0190 0191 void ConsoleKitSession::closeRestricted(int fileDescriptor) 0192 { 0193 struct stat st; 0194 if (fstat(fileDescriptor, &st) < 0) { 0195 close(fileDescriptor); 0196 return; 0197 } 0198 0199 QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, 0200 s_sessionInterface, 0201 QStringLiteral("ReleaseDevice")); 0202 // major() and minor() macros return ints on FreeBSD instead of uints. 0203 message.setArguments({uint(major(st.st_rdev)), uint(minor(st.st_rdev))}); 0204 0205 QDBusConnection::systemBus().asyncCall(message); 0206 0207 close(fileDescriptor); 0208 } 0209 0210 void ConsoleKitSession::switchTo(uint terminal) 0211 { 0212 QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_seatPath, 0213 s_seatInterface, 0214 QStringLiteral("SwitchTo")); 0215 message.setArguments({terminal}); 0216 0217 QDBusConnection::systemBus().asyncCall(message); 0218 } 0219 0220 ConsoleKitSession::ConsoleKitSession(const QString &sessionPath) 0221 : m_sessionPath(sessionPath) 0222 { 0223 qDBusRegisterMetaType<DBusConsoleKitSeat>(); 0224 } 0225 0226 ConsoleKitSession::~ConsoleKitSession() 0227 { 0228 releaseControl(m_sessionPath); 0229 } 0230 0231 bool ConsoleKitSession::initialize() 0232 { 0233 QDBusMessage activeMessage = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, 0234 s_propertiesInterface, 0235 QStringLiteral("Get")); 0236 activeMessage.setArguments({s_sessionInterface, QStringLiteral("active")}); 0237 0238 QDBusMessage seatMessage = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, 0239 s_propertiesInterface, 0240 QStringLiteral("Get")); 0241 seatMessage.setArguments({s_sessionInterface, QStringLiteral("Seat")}); 0242 0243 QDBusMessage terminalMessage = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, 0244 s_propertiesInterface, 0245 QStringLiteral("Get")); 0246 terminalMessage.setArguments({s_sessionInterface, QStringLiteral("VTNr")}); 0247 0248 QDBusPendingReply<QVariant> activeReply = 0249 QDBusConnection::systemBus().asyncCall(activeMessage); 0250 QDBusPendingReply<QVariant> terminalReply = 0251 QDBusConnection::systemBus().asyncCall(terminalMessage); 0252 QDBusPendingReply<QVariant> seatReply = 0253 QDBusConnection::systemBus().asyncCall(seatMessage); 0254 0255 // We must wait until all replies have been received because the drm backend needs a 0256 // valid seat name to properly select gpu devices, this also simplifies startup code. 0257 activeReply.waitForFinished(); 0258 terminalReply.waitForFinished(); 0259 seatReply.waitForFinished(); 0260 0261 if (activeReply.isError()) { 0262 qCWarning(KWIN_CORE) << "Failed to query active session property:" << activeReply.error(); 0263 return false; 0264 } 0265 if (terminalReply.isError()) { 0266 qCWarning(KWIN_CORE) << "Failed to query VTNr session property:" << terminalReply.error(); 0267 return false; 0268 } 0269 if (seatReply.isError()) { 0270 qCWarning(KWIN_CORE) << "Failed to query Seat session property:" << seatReply.error(); 0271 return false; 0272 } 0273 0274 m_isActive = activeReply.value().toBool(); 0275 m_terminal = terminalReply.value().toUInt(); 0276 0277 const DBusConsoleKitSeat seat = qdbus_cast<DBusConsoleKitSeat>(seatReply.value().value<QDBusArgument>()); 0278 m_seatId = seat.id; 0279 m_seatPath = seat.path.path(); 0280 0281 QDBusConnection::systemBus().connect(s_serviceName, s_managerPath, s_managerInterface, 0282 QStringLiteral("PrepareForSleep"), 0283 this, 0284 SLOT(handlePrepareForSleep(bool))); 0285 0286 QDBusConnection::systemBus().connect(s_serviceName, m_sessionPath, s_sessionInterface, 0287 QStringLiteral("PauseDevice"), 0288 this, 0289 SLOT(handlePauseDevice(uint, uint, QString))); 0290 0291 QDBusConnection::systemBus().connect(s_serviceName, m_sessionPath, s_sessionInterface, 0292 QStringLiteral("ResumeDevice"), 0293 this, 0294 SLOT(handleResumeDevice(uint, uint, QDBusUnixFileDescriptor))); 0295 0296 QDBusConnection::systemBus().connect(s_serviceName, m_sessionPath, s_propertiesInterface, 0297 QStringLiteral("PropertiesChanged"), 0298 this, 0299 SLOT(handlePropertiesChanged(QString, QVariantMap))); 0300 0301 return true; 0302 } 0303 0304 void ConsoleKitSession::updateActive(bool active) 0305 { 0306 if (m_isActive != active) { 0307 m_isActive = active; 0308 Q_EMIT activeChanged(active); 0309 } 0310 } 0311 0312 void ConsoleKitSession::handlePauseDevice(uint major, uint minor, const QString &type) 0313 { 0314 Q_EMIT devicePaused(makedev(major, minor)); 0315 0316 if (type == QLatin1String("pause")) { 0317 QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, 0318 s_sessionInterface, 0319 QStringLiteral("PauseDeviceComplete")); 0320 message.setArguments({major, minor}); 0321 0322 QDBusConnection::systemBus().asyncCall(message); 0323 } 0324 } 0325 0326 void ConsoleKitSession::handleResumeDevice(uint major, uint minor, QDBusUnixFileDescriptor fileDescriptor) 0327 { 0328 // We don't care about the file descriptor as the libinput backend will re-open input devices 0329 // and the drm file descriptors remain valid after pausing gpus. 0330 0331 Q_EMIT deviceResumed(makedev(major, minor)); 0332 } 0333 0334 void ConsoleKitSession::handlePropertiesChanged(const QString &interfaceName, const QVariantMap &properties) 0335 { 0336 if (interfaceName == s_sessionInterface) { 0337 const QVariant active = properties.value(QStringLiteral("active")); 0338 if (active.isValid()) { 0339 updateActive(active.toBool()); 0340 } 0341 } 0342 } 0343 0344 void ConsoleKitSession::handlePrepareForSleep(bool sleep) 0345 { 0346 if (!sleep) { 0347 Q_EMIT awoke(); 0348 } 0349 } 0350 0351 } // namespace KWin 0352 0353 #include "moc_session_consolekit.cpp"