KWin
Loading...
Searching...
No Matches
session_consolekit.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
8#include "utils/common.h"
9
10#include <QCoreApplication>
11#include <QDBusConnection>
12#include <QDBusConnectionInterface>
13#include <QDBusInterface>
14#include <QDBusMessage>
15#include <QDBusMetaType>
16#include <QDBusObjectPath>
17#include <QDBusPendingCall>
18#include <QDBusUnixFileDescriptor>
19
20#include <fcntl.h>
21#include <sys/stat.h>
22#include <unistd.h>
23
24#if __has_include(<sys/sysmacros.h>)
25#include <sys/sysmacros.h>
26#endif
27
28// Note that ConsoleKit's session api is not fully compatible with logind's session api.
29
31{
32 QString id;
33 QDBusObjectPath path;
34};
35
36QDBusArgument &operator<<(QDBusArgument &argument, const DBusConsoleKitSeat &seat)
37{
38 argument.beginStructure();
39 argument << seat.id << seat.path;
40 argument.endStructure();
41 return argument;
42}
43
44const QDBusArgument &operator>>(const QDBusArgument &argument, DBusConsoleKitSeat &seat)
45{
46 argument.beginStructure();
47 argument >> seat.id >> seat.path;
48 argument.endStructure();
49 return argument;
50}
51
53
54namespace KWin
55{
56
57static const QString s_serviceName = QStringLiteral("org.freedesktop.ConsoleKit");
58static const QString s_propertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties");
59static const QString s_sessionInterface = QStringLiteral("org.freedesktop.ConsoleKit.Session");
60static const QString s_seatInterface = QStringLiteral("org.freedesktop.ConsoleKit.Seat");
61static const QString s_managerInterface = QStringLiteral("org.freedesktop.ConsoleKit.Manager");
62static const QString s_managerPath = QStringLiteral("/org/freedesktop/ConsoleKit/Manager");
63
64static QString findProcessSessionPath()
65{
66 QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, s_managerPath,
67 s_managerInterface,
68 QStringLiteral("GetSessionByPID"));
69 message.setArguments({uint32_t(QCoreApplication::applicationPid())});
70
71 const QDBusMessage reply = QDBusConnection::systemBus().call(message);
72 if (reply.type() == QDBusMessage::ErrorMessage) {
73 return QString();
74 }
75
76 return reply.arguments().constFirst().value<QDBusObjectPath>().path();
77}
78
79static bool takeControl(const QString &sessionPath)
80{
81 QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, sessionPath,
82 s_sessionInterface,
83 QStringLiteral("TakeControl"));
84 message.setArguments({false});
85
86 const QDBusMessage reply = QDBusConnection::systemBus().call(message);
87
88 return reply.type() != QDBusMessage::ErrorMessage;
89}
90
91static void releaseControl(const QString &sessionPath)
92{
93 const QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, sessionPath,
94 s_sessionInterface,
95 QStringLiteral("ReleaseControl"));
96
97 QDBusConnection::systemBus().asyncCall(message);
98}
99
100static bool activate(const QString &sessionPath)
101{
102 const QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, sessionPath,
103 s_sessionInterface,
104 QStringLiteral("Activate"));
105
106 const QDBusMessage reply = QDBusConnection::systemBus().call(message);
107
108 return reply.type() != QDBusMessage::ErrorMessage;
109}
110
111std::unique_ptr<ConsoleKitSession> ConsoleKitSession::create()
112{
113 if (!QDBusConnection::systemBus().interface()->isServiceRegistered(s_serviceName)) {
114 return nullptr;
115 }
116
117 const QString sessionPath = findProcessSessionPath();
118 if (sessionPath.isEmpty()) {
119 qCWarning(KWIN_CORE) << "Could not determine the active graphical session";
120 return nullptr;
121 }
122
123 if (!activate(sessionPath)) {
124 qCWarning(KWIN_CORE, "Failed to activate %s session. Maybe another compositor is running?",
125 qPrintable(sessionPath));
126 return nullptr;
127 }
128
129 if (!takeControl(sessionPath)) {
130 qCWarning(KWIN_CORE, "Failed to take control of %s session. Maybe another compositor is running?",
131 qPrintable(sessionPath));
132 return nullptr;
133 }
134
135 std::unique_ptr<ConsoleKitSession> session{new ConsoleKitSession(sessionPath)};
136 if (session->initialize()) {
137 return session;
138 } else {
139 return nullptr;
140 }
141}
142
144{
145 return m_isActive;
146}
147
148ConsoleKitSession::Capabilities ConsoleKitSession::capabilities() const
149{
151}
152
154{
155 return m_seatId;
156}
157
159{
160 return m_terminal;
161}
162
163int ConsoleKitSession::openRestricted(const QString &fileName)
164{
165 struct stat st;
166 if (stat(fileName.toUtf8(), &st) < 0) {
167 return -1;
168 }
169
170 QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath,
171 s_sessionInterface,
172 QStringLiteral("TakeDevice"));
173 // major() and minor() macros return ints on FreeBSD instead of uints.
174 message.setArguments({uint(major(st.st_rdev)), uint(minor(st.st_rdev))});
175
176 const QDBusMessage reply = QDBusConnection::systemBus().call(message);
177 if (reply.type() == QDBusMessage::ErrorMessage) {
178 qCDebug(KWIN_CORE, "Failed to open %s device (%s)",
179 qPrintable(fileName), qPrintable(reply.errorMessage()));
180 return -1;
181 }
182
183 const QDBusUnixFileDescriptor descriptor = reply.arguments().constFirst().value<QDBusUnixFileDescriptor>();
184 if (!descriptor.isValid()) {
185 return -1;
186 }
187
188 return fcntl(descriptor.fileDescriptor(), F_DUPFD_CLOEXEC, 0);
189}
190
192{
193 struct stat st;
194 if (fstat(fileDescriptor, &st) < 0) {
195 close(fileDescriptor);
196 return;
197 }
198
199 QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath,
200 s_sessionInterface,
201 QStringLiteral("ReleaseDevice"));
202 // major() and minor() macros return ints on FreeBSD instead of uints.
203 message.setArguments({uint(major(st.st_rdev)), uint(minor(st.st_rdev))});
204
205 QDBusConnection::systemBus().asyncCall(message);
206
207 close(fileDescriptor);
208}
209
211{
212 QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_seatPath,
213 s_seatInterface,
214 QStringLiteral("SwitchTo"));
215 message.setArguments({terminal});
216
217 QDBusConnection::systemBus().asyncCall(message);
218}
219
220ConsoleKitSession::ConsoleKitSession(const QString &sessionPath)
221 : m_sessionPath(sessionPath)
222{
223 qDBusRegisterMetaType<DBusConsoleKitSeat>();
224}
225
227{
228 releaseControl(m_sessionPath);
229}
230
231bool ConsoleKitSession::initialize()
232{
233 QDBusMessage activeMessage = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath,
234 s_propertiesInterface,
235 QStringLiteral("Get"));
236 activeMessage.setArguments({s_sessionInterface, QStringLiteral("active")});
237
238 QDBusMessage seatMessage = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath,
239 s_propertiesInterface,
240 QStringLiteral("Get"));
241 seatMessage.setArguments({s_sessionInterface, QStringLiteral("Seat")});
242
243 QDBusMessage terminalMessage = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath,
244 s_propertiesInterface,
245 QStringLiteral("Get"));
246 terminalMessage.setArguments({s_sessionInterface, QStringLiteral("VTNr")});
247
248 QDBusPendingReply<QVariant> activeReply =
249 QDBusConnection::systemBus().asyncCall(activeMessage);
250 QDBusPendingReply<QVariant> terminalReply =
251 QDBusConnection::systemBus().asyncCall(terminalMessage);
252 QDBusPendingReply<QVariant> seatReply =
253 QDBusConnection::systemBus().asyncCall(seatMessage);
254
255 // We must wait until all replies have been received because the drm backend needs a
256 // valid seat name to properly select gpu devices, this also simplifies startup code.
257 activeReply.waitForFinished();
258 terminalReply.waitForFinished();
259 seatReply.waitForFinished();
260
261 if (activeReply.isError()) {
262 qCWarning(KWIN_CORE) << "Failed to query active session property:" << activeReply.error();
263 return false;
264 }
265 if (terminalReply.isError()) {
266 qCWarning(KWIN_CORE) << "Failed to query VTNr session property:" << terminalReply.error();
267 return false;
268 }
269 if (seatReply.isError()) {
270 qCWarning(KWIN_CORE) << "Failed to query Seat session property:" << seatReply.error();
271 return false;
272 }
273
274 m_isActive = activeReply.value().toBool();
275 m_terminal = terminalReply.value().toUInt();
276
277 const DBusConsoleKitSeat seat = qdbus_cast<DBusConsoleKitSeat>(seatReply.value().value<QDBusArgument>());
278 m_seatId = seat.id;
279 m_seatPath = seat.path.path();
280
281 QDBusConnection::systemBus().connect(s_serviceName, s_managerPath, s_managerInterface,
282 QStringLiteral("PrepareForSleep"),
283 this,
284 SLOT(handlePrepareForSleep(bool)));
285
286 QDBusConnection::systemBus().connect(s_serviceName, m_sessionPath, s_sessionInterface,
287 QStringLiteral("PauseDevice"),
288 this,
289 SLOT(handlePauseDevice(uint, uint, QString)));
290
291 QDBusConnection::systemBus().connect(s_serviceName, m_sessionPath, s_sessionInterface,
292 QStringLiteral("ResumeDevice"),
293 this,
294 SLOT(handleResumeDevice(uint, uint, QDBusUnixFileDescriptor)));
295
296 QDBusConnection::systemBus().connect(s_serviceName, m_sessionPath, s_propertiesInterface,
297 QStringLiteral("PropertiesChanged"),
298 this,
299 SLOT(handlePropertiesChanged(QString, QVariantMap)));
300
301 return true;
302}
303
304void ConsoleKitSession::updateActive(bool active)
305{
306 if (m_isActive != active) {
307 m_isActive = active;
308 Q_EMIT activeChanged(active);
309 }
310}
311
312void ConsoleKitSession::handlePauseDevice(uint major, uint minor, const QString &type)
313{
314 Q_EMIT devicePaused(makedev(major, minor));
315
316 if (type == QLatin1String("pause")) {
317 QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath,
318 s_sessionInterface,
319 QStringLiteral("PauseDeviceComplete"));
320 message.setArguments({major, minor});
321
322 QDBusConnection::systemBus().asyncCall(message);
323 }
324}
325
326void ConsoleKitSession::handleResumeDevice(uint major, uint minor, QDBusUnixFileDescriptor fileDescriptor)
327{
328 // We don't care about the file descriptor as the libinput backend will re-open input devices
329 // and the drm file descriptors remain valid after pausing gpus.
330
331 Q_EMIT deviceResumed(makedev(major, minor));
332}
333
334void ConsoleKitSession::handlePropertiesChanged(const QString &interfaceName, const QVariantMap &properties)
335{
336 if (interfaceName == s_sessionInterface) {
337 const QVariant active = properties.value(QStringLiteral("active"));
338 if (active.isValid()) {
339 updateActive(active.toBool());
340 }
341 }
342}
343
344void ConsoleKitSession::handlePrepareForSleep(bool sleep)
345{
346 if (!sleep) {
347 Q_EMIT awoke();
348 }
349}
350
351} // namespace KWin
352
353#include "moc_session_consolekit.cpp"
uint terminal() const override
bool isActive() const override
Capabilities capabilities() const override
QString seat() const override
void closeRestricted(int fileDescriptor) override
static std::unique_ptr< ConsoleKitSession > create()
void switchTo(uint terminal) override
int openRestricted(const QString &fileName) override
void activeChanged(bool active)
void devicePaused(dev_t deviceId)
void deviceResumed(dev_t deviceId)
Q_DECLARE_METATYPE(KWin::SwitchEvent::State)
Session::Type type
Definition session.cpp:17
const QDBusArgument & operator>>(const QDBusArgument &argument, DBusConsoleKitSeat &seat)
QDBusArgument & operator<<(QDBusArgument &argument, const DBusConsoleKitSeat &seat)