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
38
39
40static SmcConn smcConnection = nullptr;
42static bool sm_smActive;
44static int sm_saveType;
45static bool sm_cancel;
47static bool sm_isshutdown;
48static bool sm_phase2;
49static bool sm_in_phase2;
51
53
54static void resetSmState();
55static void sm_setProperty(const char *name, const char *type,
56 int num_vals, SmPropValue *vals);
57static void sm_saveYourselfCallback(SmcConn smcConn, SmPointer clientData,
58 int saveType, Bool shutdown , int interactStyle, Bool fast);
59static void sm_saveYourselfPhase2Callback(SmcConn smcConn, SmPointer clientData) ;
60static void sm_dieCallback(SmcConn smcConn, SmPointer clientData) ;
61static void sm_shutdownCancelledCallback(SmcConn smcConn, SmPointer clientData);
62static void sm_saveCompleteCallback(SmcConn smcConn, SmPointer clientData);
63static void sm_interactCallback(SmcConn smcConn, SmPointer clientData);
65
66static void resetSmState()
67{
70 sm_interactStyle = SmInteractStyleNone;
71 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
81static void 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
101static void 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
110static void 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
130 unsigned int shutdown_in_progress : 1;
131};
132
133static void 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
151 if (!sm_isshutdown) // we cannot expect a confirmation message in that case
153}
154
156{
157 if (sm_isshutdown)
159
160 // generate a new session key
161 timeval tv;
162 gettimeofday(&tv, nullptr);
163 sm->setSessionKey(QString::number(qulonglong(tv.tv_sec)) +
164 u'_' +
165 QString::number(qulonglong(tv.tv_usec)));
166
167 QStringList arguments = QCoreApplication::arguments();
168 QString argument0 = arguments.isEmpty() ? QCoreApplication::applicationFilePath()
169 : arguments.at(0);
170
171 // tell the session manager about our program in best POSIX style
172 sm_setProperty(QString::fromLatin1(SmProgram), argument0);
173 // tell the session manager about our user as well.
174 struct passwd *entryPtr = nullptr;
175#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0)
176 QVarLengthArray<char, 1024> buf(qMax<long>(sysconf(_SC_GETPW_R_SIZE_MAX), 1024L));
177 struct passwd entry;
178 while (getpwuid_r(geteuid(), &entry, buf.data(), buf.size(), &entryPtr) == ERANGE) {
179 if (buf.size() >= 32768) {
180 // too big already, fail
181 static char badusername[] = "";
182 entryPtr = &entry;
183 entry.pw_name = badusername;
184 break;
185 }
186
187 // retry with a bigger buffer
188 buf.resize(buf.size() * 2);
189 }
190#else
191 entryPtr = getpwuid(geteuid());
192#endif
193 if (entryPtr)
194 sm_setProperty(QString::fromLatin1(SmUserID), QString::fromLocal8Bit(entryPtr->pw_name));
195
196 // generate a restart and discard command that makes sense
197 QStringList restart;
198 restart << argument0 << "-session"_L1 << sm->sessionId() + u'_' + sm->sessionKey();
199
200 QFileInfo fi(QCoreApplication::applicationFilePath());
201 if (qAppName().compare(fi.fileName(), Qt::CaseInsensitive) != 0)
202 restart << "-name"_L1 << qAppName();
203 sm->setRestartCommand(restart);
204 QStringList discard;
205 sm->setDiscardCommand(discard);
206
207 switch (sm_saveType) {
208 case SmSaveBoth:
209 sm->appCommitData();
211 break; // we cancelled the shutdown, no need to save state
212 Q_FALLTHROUGH();
213 case SmSaveLocal:
214 sm->appSaveState();
215 break;
216 case SmSaveGlobal:
217 sm->appCommitData();
218 break;
219 default:
220 break;
221 }
222
223 if (sm_phase2 && !sm_in_phase2) {
224 SmcRequestSaveYourselfPhase2(smcConnection, sm_saveYourselfPhase2Callback, (SmPointer*) sm);
225 qt_sm_blockUserInput = false;
226 } else {
227 // close eventual interaction monitors and cancel the
228 // shutdown, if required. Note that we can only cancel when
229 // performing a shutdown, it does not work for checkpoints
231 SmcInteractDone(smcConnection, sm_isshutdown && sm_cancel);
232 sm_interactionActive = false;
233 } else if (sm_cancel && sm_isshutdown) {
235 SmcInteractDone(smcConnection, True);
236 sm_interactionActive = false;
237 }
238 }
239
240 // set restart and discard command in session manager
241 sm_setProperty(QString::fromLatin1(SmRestartCommand), sm->restartCommand());
242 sm_setProperty(QString::fromLatin1(SmDiscardCommand), sm->discardCommand());
243
244 // set the restart hint
245 SmPropValue prop;
246 prop.length = sizeof(int);
247 int value = sm->restartHint();
248 prop.value = (SmPointer) &value;
249 sm_setProperty(SmRestartStyleHint, SmCARD8, 1, &prop);
250
251 // we are done
252 SmcSaveYourselfDone(smcConnection, !sm_cancel);
253 }
254}
255
256static void sm_dieCallback(SmcConn smcConn, SmPointer /* clientData */)
257{
258 if (smcConn != smcConnection)
259 return;
261 QWindowSystemInterface::handleApplicationTermination<QWindowSystemInterface::SynchronousDelivery>();
262}
263
264static void sm_shutdownCancelledCallback(SmcConn smcConn, SmPointer clientData)
265{
266 if (smcConn != smcConnection)
267 return;
271}
272
273static void sm_saveCompleteCallback(SmcConn smcConn, SmPointer /*clientData */)
274{
275 if (smcConn != smcConnection)
276 return;
278}
279
280static void sm_interactCallback(SmcConn smcConn, SmPointer clientData)
281{
282 if (smcConn != smcConnection)
283 return;
286}
287
288static void sm_saveYourselfPhase2Callback(SmcConn smcConn, SmPointer clientData)
289{
290 if (smcConn != smcConnection)
291 return;
292 sm_in_phase2 = true;
294}
295
296
297void QSmSocketReceiver::socketActivated()
298{
299 IceProcessMessages(SmcGetIceConnection(smcConnection), nullptr, nullptr);
300}
301
302
303// QXcbSessionManager starts here
304
305QXcbSessionManager::QXcbSessionManager(const QString &id, const QString &key)
307 , m_eventLoop(nullptr)
308{
310 char cerror[256];
311 char* myId = nullptr;
312 QByteArray b_id = id.toLatin1();
313 char* prevId = b_id.data();
314
315 SmcCallbacks cb;
316 cb.save_yourself.callback = sm_saveYourselfCallback;
317 cb.save_yourself.client_data = (SmPointer) this;
318 cb.die.callback = sm_dieCallback;
319 cb.die.client_data = (SmPointer) this;
320 cb.save_complete.callback = sm_saveCompleteCallback;
321 cb.save_complete.client_data = (SmPointer) this;
322 cb.shutdown_cancelled.callback = sm_shutdownCancelledCallback;
323 cb.shutdown_cancelled.client_data = (SmPointer) this;
324
325 // avoid showing a warning message below
326 if (!qEnvironmentVariableIsSet("SESSION_MANAGER"))
327 return;
328
329 smcConnection = SmcOpenConnection(nullptr, nullptr, 1, 0,
330 SmcSaveYourselfProcMask |
331 SmcDieProcMask |
332 SmcSaveCompleteProcMask |
333 SmcShutdownCancelledProcMask,
334 &cb,
335 prevId,
336 &myId,
337 256, cerror);
338
339 setSessionId(QString::fromLatin1(myId));
340 ::free(myId); // it was allocated by C
341
342 QString error = QString::fromLocal8Bit(cerror);
343 if (!smcConnection)
344 qWarning("Qt: Session management error: %s", qPrintable(error));
345 else
346 sm_receiver = new QSmSocketReceiver(IceConnectionNumber(SmcGetIceConnection(smcConnection)));
347}
348
350{
351 if (smcConnection)
352 SmcCloseConnection(smcConnection, 0, nullptr);
353 smcConnection = nullptr;
354 delete sm_receiver;
355}
356
357
359{
360 return (void*) smcConnection;
361}
362
364{
366 return true;
367
369 return false;
370
371 if (sm_interactStyle == SmInteractStyleAny) {
372 sm_waitingForInteraction = SmcInteractRequest(smcConnection,
373 SmDialogNormal,
375 (SmPointer*) this);
376 }
378 QEventLoop eventLoop;
379 m_eventLoop = &eventLoop;
380 eventLoop.exec();
381 m_eventLoop = nullptr;
382
384 if (sm_smActive) { // not cancelled
386 qt_sm_blockUserInput = false;
387 return true;
388 }
389 }
390 return false;
391}
392
394{
396 return true;
397
399 return false;
400
401 if (sm_interactStyle == SmInteractStyleAny || sm_interactStyle == SmInteractStyleErrors) {
402 sm_waitingForInteraction = SmcInteractRequest(smcConnection,
403 SmDialogError,
405 (SmPointer*) this);
406 }
408 QEventLoop eventLoop;
409 m_eventLoop = &eventLoop;
410 eventLoop.exec();
411 m_eventLoop = nullptr;
412
414 if (sm_smActive) { // not cancelled
416 qt_sm_blockUserInput = false;
417 return true;
418 }
419 }
420 return false;
421}
422
424{
426 SmcInteractDone(smcConnection, False);
427 sm_interactionActive = false;
430 }
431}
432
434{
435 sm_cancel = true;
436}
437
438void QXcbSessionManager::setManagerProperty(const QString &name, const QString &value)
439{
440 sm_setProperty(name, value);
441}
442
443void QXcbSessionManager::setManagerProperty(const QString &name, const QStringList &value)
444{
445 sm_setProperty(name, value);
446}
447
449{
450 return sm_in_phase2;
451}
452
454{
455 sm_phase2 = true;
456}
457
459{
460 m_eventLoop->exit();
461}
462
463#include "qxcbsessionmanager.moc"
464
465#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
static bool sm_isshutdown
static void sm_setProperty(const char *name, const char *type, int num_vals, SmPropValue *vals)
static bool sm_smActive
static void sm_saveYourselfPhase2Callback(SmcConn smcConn, SmPointer clientData)
static bool sm_phase2
static void sm_saveYourselfCallback(SmcConn smcConn, SmPointer clientData, int saveType, Bool shutdown, int interactStyle, Bool fast)
static void sm_dieCallback(SmcConn smcConn, SmPointer clientData)
static SmcConn smcConnection
static bool sm_cancel
static void sm_saveCompleteCallback(SmcConn smcConn, SmPointer clientData)
bool qt_sm_blockUserInput
static void sm_setProperty(const QString &name, const QString &value)
static void resetSmState()
static bool sm_waitingForInteraction
static void sm_shutdownCancelledCallback(SmcConn smcConn, SmPointer clientData)
static bool sm_in_phase2
static void sm_performSaveYourself(QXcbSessionManager *)
static bool sm_interactionActive
static int sm_interactStyle
static QSmSocketReceiver * sm_receiver
static void sm_interactCallback(SmcConn smcConn, SmPointer clientData)
static int sm_saveType
unsigned int shutdown_in_progress
unsigned int save_yourself_in_progress