Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qxcbsessionmanager.cpp
Go to the documentation of this file.
1// Copyright (C) 2013 Teo Mrnjavac <teo@kde.org>
2// Copyright (C) 2016 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
7
8#ifndef QT_NO_SESSIONMANAGER
9
10#include <QtCore/qvarlengtharray.h>
11#include <qpa/qwindowsysteminterface.h>
12
13#include <qguiapplication.h>
14#include <qdatetime.h>
15#include <qfileinfo.h>
16#include <qplatformdefs.h>
17#include <qsocketnotifier.h>
18#include <X11/SM/SMlib.h>
19#include <errno.h> // ERANGE
20
21#include <cerrno> // ERANGE
22
23using namespace Qt::StringLiterals;
24
25namespace {
26
27class QSmSocketReceiver : public QObject
28{
29 Q_OBJECT
30public:
31 QSmSocketReceiver(int socket)
32 {
33 QSocketNotifier* sn = new QSocketNotifier(socket, QSocketNotifier::Read, this);
34 connect(sn, SIGNAL(activated(QSocketDescriptor)), this, SLOT(socketActivated()));
35 }
36
37public Q_SLOTS:
38 void socketActivated();
39};
40
41
42SmcConn smcConnection = nullptr;
43bool sm_interactionActive;
44bool sm_smActive;
45int sm_interactStyle;
46int sm_saveType;
47bool sm_cancel;
48bool sm_waitingForInteraction;
49bool sm_isshutdown;
50bool sm_phase2;
51bool sm_in_phase2;
52
53QSmSocketReceiver* sm_receiver = nullptr;
54
55void resetSmState();
56void sm_setProperty(const char *name, const char *type,
57 int num_vals, SmPropValue *vals);
58void sm_saveYourselfCallback(SmcConn smcConn, SmPointer clientData,
59 int saveType, Bool shutdown , int interactStyle, Bool fast);
60void sm_saveYourselfPhase2Callback(SmcConn smcConn, SmPointer clientData) ;
61void sm_dieCallback(SmcConn smcConn, SmPointer clientData) ;
62void sm_shutdownCancelledCallback(SmcConn smcConn, SmPointer clientData);
63void sm_saveCompleteCallback(SmcConn smcConn, SmPointer clientData);
64void sm_interactCallback(SmcConn smcConn, SmPointer clientData);
65void sm_performSaveYourself(QXcbSessionManager*);
66
67void resetSmState()
68{
69 sm_waitingForInteraction = false;
70 sm_interactionActive = false;
71 sm_interactStyle = SmInteractStyleNone;
72 sm_smActive = false;
73 sm_isshutdown = false;
74 sm_phase2 = false;
75 sm_in_phase2 = false;
76}
77
78
79// theoretically it's possible to set several properties at once. For
80// simplicity, however, we do just one property at a time
81void sm_setProperty(const char *name, const char *type,
82 int num_vals, SmPropValue *vals)
83{
84 if (num_vals) {
85 SmProp prop;
86 prop.name = const_cast<char*>(name);
87 prop.type = const_cast<char*>(type);
88 prop.num_vals = num_vals;
89 prop.vals = vals;
90
91 SmProp* props[1];
92 props[0] = &prop;
93 SmcSetProperties(smcConnection, 1, props);
94 } else {
95 char* names[1];
96 names[0] = const_cast<char*>(name);
97 SmcDeleteProperties(smcConnection, 1, names);
98 }
99}
100
101void sm_setProperty(const QString &name, const QString &value)
102{
103 QByteArray v = value.toUtf8();
104 SmPropValue prop;
105 prop.length = v.size();
106 prop.value = (SmPointer) const_cast<char *>(v.constData());
107 sm_setProperty(name.toLatin1().data(), SmARRAY8, 1, &prop);
108}
109
110void sm_setProperty(const QString &name, const QStringList &value)
111{
112 SmPropValue *prop = new SmPropValue[value.size()];
113 int count = 0;
114 QList<QByteArray> vl;
115 vl.reserve(value.size());
116 for (QStringList::ConstIterator it = value.begin(); it != value.end(); ++it) {
117 prop[count].length = (*it).size();
118 vl.append((*it).toUtf8());
119 prop[count].value = (char*)vl.constLast().data();
120 ++count;
121 }
122 sm_setProperty(name.toLatin1().data(), SmLISTofARRAY8, count, prop);
123 delete [] prop;
124}
125
126
127// workaround for broken libsm, see below
128struct QT_smcConn {
129 unsigned int save_yourself_in_progress : 1;
130 unsigned int shutdown_in_progress : 1;
131};
132
133void sm_saveYourselfCallback(SmcConn smcConn, SmPointer clientData,
134 int saveType, Bool shutdown , int interactStyle, Bool /*fast*/)
135{
136 if (smcConn != smcConnection)
137 return;
138 sm_cancel = false;
139 sm_smActive = true;
140 sm_isshutdown = shutdown;
141 sm_saveType = saveType;
142 sm_interactStyle = interactStyle;
143
144 // ugly workaround for broken libSM. libSM should do that _before_
145 // actually invoking the callback in sm_process.c
146 ((QT_smcConn*)smcConn)->save_yourself_in_progress = true;
147 if (sm_isshutdown)
148 ((QT_smcConn*)smcConn)->shutdown_in_progress = true;
149
150 sm_performSaveYourself((QXcbSessionManager*) clientData);
151 if (!sm_isshutdown) // we cannot expect a confirmation message in that case
152 resetSmState();
153}
154
155void sm_performSaveYourself(QXcbSessionManager *sm)
156{
157 // generate a new session key
158 timeval tv;
159 gettimeofday(&tv, nullptr);
160 sm->setSessionKey(QString::number(qulonglong(tv.tv_sec)) +
161 u'_' +
162 QString::number(qulonglong(tv.tv_usec)));
163
164 QStringList arguments = QCoreApplication::arguments();
165 QString argument0 = arguments.isEmpty() ? QCoreApplication::applicationFilePath()
166 : arguments.at(0);
167
168 // tell the session manager about our program in best POSIX style
169 sm_setProperty(QString::fromLatin1(SmProgram), argument0);
170 // tell the session manager about our user as well.
171 struct passwd *entryPtr = nullptr;
172#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0)
173 QVarLengthArray<char, 1024> buf(qMax<long>(sysconf(_SC_GETPW_R_SIZE_MAX), 1024L));
174 struct passwd entry;
175 while (getpwuid_r(geteuid(), &entry, buf.data(), buf.size(), &entryPtr) == ERANGE) {
176 if (buf.size() >= 32768) {
177 // too big already, fail
178 static char badusername[] = "";
179 entryPtr = &entry;
180 entry.pw_name = badusername;
181 break;
182 }
183
184 // retry with a bigger buffer
185 buf.resize(buf.size() * 2);
186 }
187#else
188 entryPtr = getpwuid(geteuid());
189#endif
190 if (entryPtr)
191 sm_setProperty(QString::fromLatin1(SmUserID), QString::fromLocal8Bit(entryPtr->pw_name));
192
193 // generate a restart and discard command that makes sense
194 QStringList restart;
195 restart << argument0 << "-session"_L1 << sm->sessionId() + u'_' + sm->sessionKey();
196
197 QFileInfo fi(QCoreApplication::applicationFilePath());
198 if (qAppName().compare(fi.fileName(), Qt::CaseInsensitive) != 0)
199 restart << "-name"_L1 << qAppName();
200 sm->setRestartCommand(restart);
201 QStringList discard;
202 sm->setDiscardCommand(discard);
203
204 switch (sm_saveType) {
205 case SmSaveBoth:
206 sm->appCommitData();
207 if (sm_isshutdown && sm_cancel)
208 break; // we cancelled the shutdown, no need to save state
209 Q_FALLTHROUGH();
210 case SmSaveLocal:
211 sm->appSaveState();
212 break;
213 case SmSaveGlobal:
214 sm->appCommitData();
215 break;
216 default:
217 break;
218 }
219
220 if (sm_phase2 && !sm_in_phase2) {
221 SmcRequestSaveYourselfPhase2(smcConnection, sm_saveYourselfPhase2Callback, (SmPointer*) sm);
222 } else {
223 // close eventual interaction monitors and cancel the
224 // shutdown, if required. Note that we can only cancel when
225 // performing a shutdown, it does not work for checkpoints
226 if (sm_interactionActive) {
227 SmcInteractDone(smcConnection, sm_isshutdown && sm_cancel);
228 sm_interactionActive = false;
229 } else if (sm_cancel && sm_isshutdown) {
231 SmcInteractDone(smcConnection, True);
232 sm_interactionActive = false;
233 }
234 }
235
236 // set restart and discard command in session manager
237 sm_setProperty(QString::fromLatin1(SmRestartCommand), sm->restartCommand());
238 sm_setProperty(QString::fromLatin1(SmDiscardCommand), sm->discardCommand());
239
240 // set the restart hint
241 SmPropValue prop;
242 prop.length = sizeof(int);
243 int value = sm->restartHint();
244 prop.value = (SmPointer) &value;
245 sm_setProperty(SmRestartStyleHint, SmCARD8, 1, &prop);
246
247 // we are done
248 SmcSaveYourselfDone(smcConnection, !sm_cancel);
249 }
250}
251
252void sm_dieCallback(SmcConn smcConn, SmPointer /* clientData */)
253{
254 if (smcConn != smcConnection)
255 return;
256 resetSmState();
257 QWindowSystemInterface::handleApplicationTermination<QWindowSystemInterface::SynchronousDelivery>();
258}
259
260void sm_shutdownCancelledCallback(SmcConn smcConn, SmPointer clientData)
261{
262 if (smcConn != smcConnection)
263 return;
264 if (sm_waitingForInteraction)
266 resetSmState();
267}
268
269void sm_saveCompleteCallback(SmcConn smcConn, SmPointer /*clientData */)
270{
271 if (smcConn != smcConnection)
272 return;
273 resetSmState();
274}
275
276void sm_interactCallback(SmcConn smcConn, SmPointer clientData)
277{
278 if (smcConn != smcConnection)
279 return;
280 if (sm_waitingForInteraction)
282}
283
284void sm_saveYourselfPhase2Callback(SmcConn smcConn, SmPointer clientData)
285{
286 if (smcConn != smcConnection)
287 return;
288 sm_in_phase2 = true;
289 sm_performSaveYourself((QXcbSessionManager *) clientData);
290}
291
292void QSmSocketReceiver::socketActivated()
293{
294 IceProcessMessages(SmcGetIceConnection(smcConnection), nullptr, nullptr);
295}
296
297} // namespace
298
299// QXcbSessionManager starts here
300
301QXcbSessionManager::QXcbSessionManager(const QString &id, const QString &key)
303 , m_eventLoop(nullptr)
304{
305 resetSmState();
306 char cerror[256];
307 char* myId = nullptr;
308 QByteArray b_id = id.toLatin1();
309 char* prevId = b_id.data();
310
311 SmcCallbacks cb;
312 cb.save_yourself.callback = sm_saveYourselfCallback;
313 cb.save_yourself.client_data = (SmPointer) this;
314 cb.die.callback = sm_dieCallback;
315 cb.die.client_data = (SmPointer) this;
316 cb.save_complete.callback = sm_saveCompleteCallback;
317 cb.save_complete.client_data = (SmPointer) this;
318 cb.shutdown_cancelled.callback = sm_shutdownCancelledCallback;
319 cb.shutdown_cancelled.client_data = (SmPointer) this;
320
321 // avoid showing a warning message below
322 if (!qEnvironmentVariableIsSet("SESSION_MANAGER"))
323 return;
324
325 smcConnection = SmcOpenConnection(nullptr, nullptr, 1, 0,
326 SmcSaveYourselfProcMask |
327 SmcDieProcMask |
328 SmcSaveCompleteProcMask |
329 SmcShutdownCancelledProcMask,
330 &cb,
331 prevId,
332 &myId,
333 256, cerror);
334
335 setSessionId(QString::fromLatin1(myId));
336 ::free(myId); // it was allocated by C
337
338 QString error = QString::fromLocal8Bit(cerror);
339 if (!smcConnection)
340 qWarning("Qt: Session management error: %s", qPrintable(error));
341 else
342 sm_receiver = new QSmSocketReceiver(IceConnectionNumber(SmcGetIceConnection(smcConnection)));
343}
344
346{
347 if (smcConnection)
348 SmcCloseConnection(smcConnection, 0, nullptr);
349 smcConnection = nullptr;
350 delete sm_receiver;
351}
352
353
355{
356 return (void*) smcConnection;
357}
358
360{
361 if (sm_interactionActive)
362 return true;
363
364 if (sm_waitingForInteraction)
365 return false;
366
367 if (sm_interactStyle == SmInteractStyleAny) {
368 sm_waitingForInteraction = SmcInteractRequest(smcConnection,
369 SmDialogNormal,
370 sm_interactCallback,
371 (SmPointer*) this);
372 }
373 if (sm_waitingForInteraction) {
374 QEventLoop eventLoop;
375 m_eventLoop = &eventLoop;
376 eventLoop.exec();
377 m_eventLoop = nullptr;
378
379 sm_waitingForInteraction = false;
380 if (sm_smActive) { // not cancelled
381 sm_interactionActive = true;
382 return true;
383 }
384 }
385 return false;
386}
387
389{
390 if (sm_interactionActive)
391 return true;
392
393 if (sm_waitingForInteraction)
394 return false;
395
396 if (sm_interactStyle == SmInteractStyleAny || sm_interactStyle == SmInteractStyleErrors) {
397 sm_waitingForInteraction = SmcInteractRequest(smcConnection,
398 SmDialogError,
399 sm_interactCallback,
400 (SmPointer*) this);
401 }
402 if (sm_waitingForInteraction) {
403 QEventLoop eventLoop;
404 m_eventLoop = &eventLoop;
405 eventLoop.exec();
406 m_eventLoop = nullptr;
407
408 sm_waitingForInteraction = false;
409 if (sm_smActive) { // not cancelled
410 sm_interactionActive = true;
411 return true;
412 }
413 }
414 return false;
415}
416
418{
419 if (sm_interactionActive) {
420 SmcInteractDone(smcConnection, False);
421 sm_interactionActive = false;
422 }
423}
424
426{
427 sm_cancel = true;
428}
429
430void QXcbSessionManager::setManagerProperty(const QString &name, const QString &value)
431{
432 sm_setProperty(name, value);
433}
434
435void QXcbSessionManager::setManagerProperty(const QString &name, const QStringList &value)
436{
437 sm_setProperty(name, value);
438}
439
441{
442 return sm_in_phase2;
443}
444
446{
447 sm_phase2 = true;
448}
449
451{
452 m_eventLoop->exit();
453}
454
455#include "qxcbsessionmanager.moc"
456
457#endif
QXcbSessionManager(const QString &id, const QString &key)
void requestPhase2() override
bool isPhase2() const override
void setManagerProperty(const QString &name, const QString &value) override
bool allowsInteraction() override
bool allowsErrorInteraction() override