1// Copyright (C) 2022 Intel Corporation.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qtipccommon.h"
5#include "qtipccommon_p.h"
6
7#include <qcryptographichash.h>
8#include <qstandardpaths.h>
9#include <qstringconverter.h>
10#include <qurl.h>
11#include <qurlquery.h>
12
13#if defined(Q_OS_DARWIN)
14# include "private/qcore_mac_p.h"
15# if !defined(SHM_NAME_MAX)
16 // Based on PSEMNAMLEN in XNU's posix_sem.c, which would
17 // indicate the max length is 31, _excluding_ the zero
18 // terminator. But in practice (possibly due to an off-
19 // by-one bug in the kernel) the usable bytes are only 30.
20# define SHM_NAME_MAX 30
21# endif
22#elif defined(Q_OS_WINDOWS)
23# include "qt_windows.h"
24#endif
25
26#if QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
27
28QT_BEGIN_NAMESPACE
29
30using namespace Qt::StringLiterals;
31
32static QStringView staticTypeToString(QNativeIpcKey::Type type)
33{
34 switch (type) {
35 case QNativeIpcKey::Type::SystemV:
36 return u"systemv";
37 case QNativeIpcKey::Type::PosixRealtime:
38 return u"posix";
39 case QNativeIpcKey::Type::Windows:
40 return u"windows";
41 }
42 return {};
43}
44
45static QString typeToString(QNativeIpcKey::Type type)
46{
47 QStringView typeString = staticTypeToString(type);
48 switch (type) {
49 case QNativeIpcKey::Type::SystemV:
50 case QNativeIpcKey::Type::PosixRealtime:
51 case QNativeIpcKey::Type::Windows:
52 return QString::fromRawData(typeString.constData(), size: typeString.size());
53 }
54
55 int value = int(type);
56 if (value >= 1 && value <= 0xff) {
57 // System V key with id different from 'Q'
58 typeString = staticTypeToString(type: QNativeIpcKey::Type::SystemV);
59 return typeString + QString::number(-value); // negative so it prepends a dash
60 }
61
62 return QString(); // invalid!
63}
64
65static QNativeIpcKey::Type stringToType(QStringView typeString)
66{
67 if (typeString == staticTypeToString(type: QNativeIpcKey::Type::PosixRealtime))
68 return QNativeIpcKey::Type::PosixRealtime;
69 if (typeString == staticTypeToString(type: QNativeIpcKey::Type::Windows))
70 return QNativeIpcKey::Type::Windows;
71
72 auto fromNumber = [](QStringView number, int low, int high) {
73 bool ok;
74 int n = -number.toInt(ok: &ok, base: 10);
75 if (!ok || n < low || n > high)
76 return QNativeIpcKey::Type{};
77 return QNativeIpcKey::Type(n);
78 };
79
80 QStringView sysv = staticTypeToString(type: QNativeIpcKey::Type::SystemV);
81 if (typeString.startsWith(s: sysv)) {
82 if (typeString.size() == sysv.size())
83 return QNativeIpcKey::Type::SystemV;
84 return fromNumber(typeString.sliced(pos: sysv.size()), 1, 0xff);
85 }
86
87 // invalid!
88 return QNativeIpcKey::Type{};
89}
90
91/*!
92 \internal
93
94 Legacy: this exists for compatibility with QSharedMemory and
95 QSystemSemaphore between 4.4 and 6.6.
96
97 Returns a QNativeIpcKey that contains a platform-safe key using rules
98 similar to QtIpcCommon::platformSafeKey() below, but using an algorithm
99 that is compatible with Qt 4.4 to 6.6. Additionally, the returned
100 QNativeIpcKey will record the input \a key so it can be included in the
101 string form if necessary to pass to other processes.
102*/
103QNativeIpcKey QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType,
104 QNativeIpcKey::Type type)
105{
106 QNativeIpcKey k(type);
107 if (key.isEmpty())
108 return k;
109
110 QByteArray hex = QCryptographicHash::hash(data: key.toUtf8(), method: QCryptographicHash::Sha1).toHex();
111
112 if (type == QNativeIpcKey::Type::PosixRealtime) {
113#if defined(Q_OS_DARWIN)
114 if (qt_apple_isSandboxed()) {
115 // Sandboxed applications on Apple platforms require the shared memory name
116 // to be in the form <application group identifier>/<custom identifier>.
117 // Since we don't know which application group identifier the user wants
118 // to apply, we instead document that requirement, and use the key directly.
119 QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, key, key);
120 } else {
121 // The shared memory name limit on Apple platforms is very low (30 characters),
122 // so we can't use the logic below of combining the prefix, key, and a hash,
123 // to ensure a unique and valid name. Instead we use the first part of the
124 // hash, which should still long enough to avoid collisions in practice.
125 QString native = u'/' + QLatin1StringView(hex).left(SHM_NAME_MAX - 1);
126 QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, native, key);
127 }
128 return k;
129#endif
130 }
131
132 QString result;
133 result.reserve(asize: 1 + 18 + key.size() + 40);
134 switch (ipcType) {
135 case IpcType::SharedMemory:
136 result += "qipc_sharedmemory_"_L1;
137 break;
138 case IpcType::SystemSemaphore:
139 result += "qipc_systemsem_"_L1;
140 break;
141 }
142
143 for (QChar ch : key) {
144 if ((ch >= u'a' && ch <= u'z') ||
145 (ch >= u'A' && ch <= u'Z'))
146 result += ch;
147 }
148 result.append(s: QLatin1StringView(hex));
149
150 switch (type) {
151 case QNativeIpcKey::Type::Windows:
152 if (isIpcSupported(ipcType, type: QNativeIpcKey::Type::Windows))
153 QNativeIpcKeyPrivate::setNativeAndLegacyKeys(key&: k, nativeKey: result, legacyKey: key);
154 return k;
155 case QNativeIpcKey::Type::PosixRealtime:
156 result.prepend(c: u'/');
157 if (isIpcSupported(ipcType, type: QNativeIpcKey::Type::PosixRealtime))
158 QNativeIpcKeyPrivate::setNativeAndLegacyKeys(key&: k, nativeKey: result, legacyKey: key);
159 return k;
160 case QNativeIpcKey::Type::SystemV:
161 break;
162 }
163 if (isIpcSupported(ipcType, type: QNativeIpcKey::Type::SystemV)) {
164 result = QStandardPaths::writableLocation(type: QStandardPaths::TempLocation) + u'/' + result;
165 QNativeIpcKeyPrivate::setNativeAndLegacyKeys(key&: k, nativeKey: result, legacyKey: key);
166 }
167 return k;
168}
169
170/*!
171 \internal
172 Returns a QNativeIpcKey of type \a type, suitable for QSystemSemaphore or
173 QSharedMemory depending on \a ipcType. The returned native key is generated
174 from the Unicode input \a key and is safe for use on for the key type in
175 question in the current OS.
176*/
177QNativeIpcKey QtIpcCommon::platformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType,
178 QNativeIpcKey::Type type)
179{
180 QNativeIpcKey k(type);
181 if (key.isEmpty())
182 return k;
183
184 switch (type) {
185 case QNativeIpcKey::Type::PosixRealtime:
186 if (isIpcSupported(ipcType, type: QNativeIpcKey::Type::PosixRealtime)) {
187#ifdef SHM_NAME_MAX
188 // The shared memory name limit on Apple platforms is very low (30
189 // characters), so we have to cut it down to avoid ENAMETOOLONG. We
190 // hope that there won't be too many collisions...
191 k.setNativeKey(u'/' + QStringView(key).left(SHM_NAME_MAX - 1));
192#else
193 k.setNativeKey(u'/' + key);
194#endif
195 }
196 return k;
197
198 case QNativeIpcKey::Type::Windows:
199 if (isIpcSupported(ipcType, type: QNativeIpcKey::Type::Windows)) {
200 QStringView prefix;
201 QStringView payload = key;
202 // see https://learn.microsoft.com/en-us/windows/win32/termserv/kernel-object-namespaces
203 for (QStringView candidate : { u"Local\\", u"Global\\" }) {
204 if (!key.startsWith(s: candidate))
205 continue;
206 prefix = candidate;
207 payload = payload.sliced(pos: prefix.size());
208 break;
209 }
210
211 QStringView mid;
212 switch (ipcType) {
213 case IpcType::SharedMemory: mid = u"shm_"; break;
214 case IpcType::SystemSemaphore: mid = u"sem_"; break;
215 }
216
217 QString result = prefix + mid + payload;
218#ifdef Q_OS_WINDOWS
219 result.truncate(MAX_PATH);
220#endif
221 k.setNativeKey(result);
222 }
223 return k;
224
225 case QNativeIpcKey::Type::SystemV:
226 break;
227 }
228
229 // System V
230 if (isIpcSupported(ipcType, type: QNativeIpcKey::Type::SystemV)) {
231 if (key.startsWith(c: u'/')) {
232 k.setNativeKey(key);
233 } else {
234 QString baseDir = QStandardPaths::writableLocation(type: QStandardPaths::RuntimeLocation);
235 k.setNativeKey(baseDir + u'/' + key);
236 }
237 }
238 return k;
239}
240
241/*!
242 \class QNativeIpcKey
243 \inmodule QtCore
244 \since 6.6
245 \brief The QNativeIpcKey class holds a native key used by QSystemSemaphore and QSharedMemory.
246
247 \compares equality
248
249 The \l QSharedMemory and \l QSystemSemaphore classes identify their
250 resource using a system-wide identifier known as a "key". The low-level key
251 value as well as the key type are encapsulated in Qt using the \l
252 QNativeIpcKey class.
253
254 Those two classes also provide the means to create native keys from a
255 cross-platform identifier, using QSharedMemory::platformSafeKey() and
256 QSystemSemaphore::platformSafeKey(). Applications should never share the
257 input to those functions, as different versions of Qt may perform different
258 transformations, resulting in different native keys. Instead, the
259 application that created the IPC object should communicate the resulting
260 native key using the methods described below.
261
262 For details on the key types, platform-specific limitations, and
263 interoperability with older or non-Qt applications, see the \l{Native IPC
264 Keys} documentation. That includes important information for sandboxed
265 applications on Apple platforms, including all apps obtained via the Apple
266 App Store.
267
268 \section1 Communicating keys to other processes
269 \section2 Communicating keys to other Qt processes
270
271 If the other process supports QNativeIpcKey, the best way of communicating
272 is via the string representation obtained from toString() and parsing it
273 using fromString(). This representation can be stored on a file whose name
274 is well-known or passed on the command-line to a child process using
275 QProcess::setArguments().
276
277 If the other process does not support QNativeIpcKey, then the two processes
278 can exchange the nativeKey() but the older code is likely unable to adjust
279 its key type. The legacyDefaultTypeForOs() function returns the type that
280 legacy code used, which may not match the \l{DefaultTypeForOs} constant.
281 This is still true even if the old application is not using the same build
282 as the new one (for example, it is a Qt 5 application), provided the
283 options passed to the Qt configure script are the same.
284
285 \section2 Communicating keys to non-Qt processes
286
287 When communicating with non-Qt processes, the application must arrange to
288 obtain the key type the other process is using. This is important
289 particularly on Unix systems, where both \l PosixRealtime and \l SystemV
290 are common.
291
292 \section1 String representation of native keys
293
294 The format of the string representation of a QNativeIpcKey is meant to be
295 stable and therefore backwards and forwards compatible, provided the key
296 type is supported by the Qt version in question. That is to say, an older
297 Qt will fail to parse the string representation of a key type introduced
298 after it was released. However, successfully parsing a string
299 representation does not imply the Qt classes can successfully create an
300 object of that type; applications should verify support using
301 QSharedMemory::isKeyTypeSupported() and QSystemSemaphore::isKeyTypeSupported().
302
303 The format of the string representation is formed by two components,
304 separated by a colon (':'). The first component is the key type, described
305 in the table below. The second component is a type-specific payload, using
306 \l{QByteArray::fromPercentEncoding}{percent-encoding}. For all currently
307 supported key types, the decoded form is identical to the contents of the
308 nativeKey() field.
309
310 \table
311 \row \li Key type \li String representation
312 \row \li \l PosixRealtime \li \c "posix"
313 \row \li \l SystemV \li \c "systemv"
314 \row \li \l Windows \li \c "windows"
315 \row \li Non-standard SystemV \li \c "systemv-" followed by a decimal number
316 \endtable
317
318 This format resembles a URI and allows parsing using URI/URL-parsing
319 functions, such as \l QUrl. When parsed by such API, the key type will show
320 up as the \l{QUrl::scheme()}{scheme}, and the payload will be the
321 \l{QUrl::path()}{path}. Use of query or fragments is reserved.
322
323 \sa QSharedMemory, QSystemSemaphore
324*/
325
326/*!
327 \enum QNativeIpcKey::Type
328
329 This enum describes the backend type for the IPC object. For details on the
330 key types, see the \l{Native IPC Keys} documentation.
331
332 \value SystemV X/Open System Initiative (XSI) or System V (SVr4) API
333 \value PosixRealtime IEEE 1003.1b (POSIX.1b) API
334 \value Windows Win32 API
335
336 \sa setType(), type()
337*/
338
339/*!
340 \variable QNativeIpcKey::DefaultTypeForOs
341
342 This constant expression variable holds the default native IPC type for the
343 current OS. It will be Type::Windows for Windows systems and
344 Type::PosixRealtime elsewhere. Note that this constant is different from
345 what \l QSharedMemory and \l QSystemSemaphore defaulted to on the majority
346 of Unix systems prior to Qt 6.6; see legacyDefaultTypeForOs() for more
347 information.
348*/
349
350/*!
351 \fn QNativeIpcKey::legacyDefaultTypeForOs() noexcept
352
353 Returns the \l{Type} that corresponds to the native IPC key that
354 \l{QSharedMemory} and \l{QSystemSemaphore} used to use prior to Qt 6.6.
355 Applications and libraries that must retain compatibility with code using
356 either class that was compiled with Qt prior to version 6.6 can use this
357 function to determine what IPC type the other applications may be using.
358
359 Note that this function relies on Qt having been built with identical
360 configure-time options.
361*/
362#if defined(Q_OS_DARWIN)
363QNativeIpcKey::Type QNativeIpcKey::defaultTypeForOs_internal() noexcept
364{
365 if (qt_apple_isSandboxed())
366 return Type::PosixRealtime;
367 return Type::SystemV;
368}
369#endif
370
371/*!
372 \fn QNativeIpcKey::QNativeIpcKey() noexcept
373
374 Constructs a QNativeIpcKey object of type \l DefaultTypeForOs with an empty key.
375*/
376
377/*!
378 \fn QNativeIpcKey::QNativeIpcKey(Type type) noexcept
379 \fn QNativeIpcKey::QNativeIpcKey(const QString &key, Type type)
380
381 Constructs a QNativeIpcKey object holding native key \a key (or empty on
382 the overload without the parameter) for type \a type.
383*/
384
385/*!
386 \fn QNativeIpcKey::QNativeIpcKey(const QNativeIpcKey &other)
387 \fn QNativeIpcKey::QNativeIpcKey(QNativeIpcKey &&other) noexcept
388 \fn QNativeIpcKey &QNativeIpcKey::operator=(const QNativeIpcKey &other)
389 \fn QNativeIpcKey &QNativeIpcKey::operator=(QNativeIpcKey &&other) noexcept
390
391 Copies or moves the content of \a other.
392*/
393void QNativeIpcKey::copy_internal(const QNativeIpcKey &other)
394{
395 d = new QNativeIpcKeyPrivate(*other.d);
396}
397
398void QNativeIpcKey::move_internal(QNativeIpcKey &&) noexcept
399{
400 // inline code already moved properly, nothing for us to do here
401}
402
403QNativeIpcKey &QNativeIpcKey::assign_internal(const QNativeIpcKey &other)
404{
405 Q_ASSERT(d || other.d); // only 3 cases to handle
406 if (d && !other.d)
407 *d = {};
408 else if (d)
409 *d = *other.d;
410 else
411 d = new QNativeIpcKeyPrivate(*other.d);
412 return *this;
413}
414
415/*!
416 \fn QNativeIpcKey::~QNativeIpcKey()
417
418 Disposes of this QNativeIpcKey object.
419*/
420void QNativeIpcKey::destroy_internal() noexcept
421{
422 delete d;
423}
424
425/*!
426 \fn QNativeIpcKey::swap(QNativeIpcKey &other) noexcept
427 \memberswap{native IPC key and type}
428*/
429
430/*!
431 \fn swap(QNativeIpcKey &value1, QNativeIpcKey &value2) noexcept
432 \relates QNativeIpcKey
433
434 Swaps the native IPC key and type \a value1 with \a value2.
435 This operation is very fast and never fails.
436*/
437
438/*!
439 \fn QNativeIpcKey::isEmpty() const
440
441 Returns true if the nativeKey() is empty.
442
443 \sa nativeKey()
444*/
445
446/*!
447 \fn QNativeIpcKey::isValid() const
448
449 Returns true if this object contains a valid native IPC key type. Invalid
450 types are usually the result of a failure to parse a string representation
451 using fromString().
452
453 This function performs no check on the whether the key string is actually
454 supported or valid for the current operating system.
455
456 \sa type(), fromString()
457*/
458
459/*!
460 \fn QNativeIpcKey::type() const noexcept
461
462 Returns the key type associated with this object.
463
464 \sa nativeKey(), setType()
465*/
466
467/*!
468 \fn QNativeIpcKey::setType(Type type)
469
470 Sets the IPC type of this object to \a type.
471
472 \sa type(), setNativeKey()
473*/
474void QNativeIpcKey::setType_internal(Type type)
475{
476 Q_UNUSED(type);
477}
478
479/*!
480 \fn QNativeIpcKey::nativeKey() const noexcept
481
482 Returns the native key string associated with this object.
483
484 \sa setNativeKey(), type()
485*/
486
487/*!
488 \fn QNativeIpcKey::setNativeKey(const QString &newKey)
489
490 Sets the native key for this object to \a newKey.
491
492 \sa nativeKey(), setType()
493*/
494void QNativeIpcKey::setNativeKey_internal(const QString &)
495{
496 d->legacyKey_.clear();
497}
498
499/*!
500 \fn size_t QNativeIpcKey::qHash(const QNativeIpcKey &key, size_t seed)
501 \qhash{QNativeIpcKey}
502*/
503size_t qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept
504{
505 // by *choice*, we're not including d->legacyKey_ in the hash -- it's
506 // already partially encoded in the key
507 return qHashMulti(seed, args: ipcKey.key, args: ipcKey.type());
508}
509
510/*!
511 \fn bool QNativeIpcKey::operator==(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
512 \fn bool QNativeIpcKey::operator!=(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
513
514 Returns true if the \a lhs and \a rhs objects hold the same (or different) contents.
515*/
516int QNativeIpcKey::compare_internal(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
517{
518 return (QNativeIpcKeyPrivate::legacyKey(key: lhs) == QNativeIpcKeyPrivate::legacyKey(key: rhs)) ? 0 : 1;
519}
520
521/*!
522 Returns the string representation of this object. String representations
523 are useful to inform other processes of the key this process created and
524 that they should attach to.
525
526 This function returns a null string if the current object is
527 \l{isValid()}{invalid}.
528
529 \sa fromString()
530*/
531QString QNativeIpcKey::toString() const
532{
533 QString prefix = typeToString(type: type());
534 if (prefix.isEmpty()) {
535 Q_ASSERT(prefix.isNull());
536 return prefix;
537 }
538
539 QString copy = nativeKey();
540 copy.replace(c: u'%', after: "%25"_L1);
541 if (copy.startsWith(s: "//"_L1))
542 copy.replace(i: 0, len: 2, after: u"/%2F"_s); // ensure it's parsed as a URL path
543
544 QUrl u;
545 u.setScheme(prefix);
546 u.setPath(path: copy, mode: QUrl::TolerantMode);
547 if (isSlowPath()) {
548 QUrlQuery q;
549 if (!d->legacyKey_.isEmpty())
550 q.addQueryItem(key: u"legacyKey"_s, value: QString(d->legacyKey_).replace(c: u'%', after: "%25"_L1));
551 u.setQuery(q);
552 }
553 return u.toString(options: QUrl::DecodeReserved);
554}
555
556/*!
557 Parses the string form \a text and returns the corresponding QNativeIpcKey.
558 String representations are useful to inform other processes of the key this
559 process created and they should attach to.
560
561 If the string could not be parsed, this function returns an
562 \l{isValid()}{invalid} object.
563
564 \sa toString(), isValid()
565*/
566QNativeIpcKey QNativeIpcKey::fromString(const QString &text)
567{
568 QUrl u(text, QUrl::TolerantMode);
569 Type invalidType = {};
570 Type type = stringToType(typeString: u.scheme());
571 if (type == invalidType || !u.isValid() || !u.userInfo().isEmpty() || !u.host().isEmpty()
572 || u.port() != -1)
573 return QNativeIpcKey(invalidType);
574
575 QNativeIpcKey result(QString(), type);
576 if (result.type() != type) // range check, just in case
577 return QNativeIpcKey(invalidType);
578
579 // decode the payload
580 result.setNativeKey(u.path());
581
582 if (u.hasQuery()) {
583 const QList items = QUrlQuery(u).queryItems();
584 for (const auto &item : items) {
585 if (item.first == u"legacyKey"_s) {
586 QString legacyKey = QUrl::fromPercentEncoding(item.second.toUtf8());
587 QNativeIpcKeyPrivate::setLegacyKey(key&: result, legacyKey: std::move(legacyKey));
588 } else {
589 // unknown query item
590 return QNativeIpcKey(invalidType);
591 }
592 }
593 }
594 return result;
595}
596
597QT_END_NAMESPACE
598
599#include "moc_qtipccommon.cpp"
600
601#endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
602

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtbase/src/corelib/ipc/qtipccommon.cpp