summaryrefslogtreecommitdiffstats
path: root/src/corelib/ipc/qtipccommon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/ipc/qtipccommon.cpp')
-rw-r--r--src/corelib/ipc/qtipccommon.cpp610
1 files changed, 610 insertions, 0 deletions
diff --git a/src/corelib/ipc/qtipccommon.cpp b/src/corelib/ipc/qtipccommon.cpp
new file mode 100644
index 0000000000..b2ae9172fa
--- /dev/null
+++ b/src/corelib/ipc/qtipccommon.cpp
@@ -0,0 +1,610 @@
+// Copyright (C) 2022 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qtipccommon.h"
+#include "qtipccommon_p.h"
+
+#include <qcryptographichash.h>
+#include <qstandardpaths.h>
+#include <qstringconverter.h>
+#include <qurl.h>
+#include <qurlquery.h>
+
+#if defined(Q_OS_DARWIN)
+# include "private/qcore_mac_p.h"
+# if !defined(SHM_NAME_MAX)
+ // Based on PSEMNAMLEN in XNU's posix_sem.c, which would
+ // indicate the max length is 31, _excluding_ the zero
+ // terminator. But in practice (possibly due to an off-
+ // by-one bug in the kernel) the usable bytes are only 30.
+# define SHM_NAME_MAX 30
+# endif
+#elif defined(Q_OS_WINDOWS)
+# include "qt_windows.h"
+#endif
+
+#if QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+static QStringView staticTypeToString(QNativeIpcKey::Type type)
+{
+ switch (type) {
+ case QNativeIpcKey::Type::SystemV:
+ return u"systemv";
+ case QNativeIpcKey::Type::PosixRealtime:
+ return u"posix";
+ case QNativeIpcKey::Type::Windows:
+ return u"windows";
+ }
+ return {};
+}
+
+static QString typeToString(QNativeIpcKey::Type type)
+{
+ QStringView typeString = staticTypeToString(type);
+ switch (type) {
+ case QNativeIpcKey::Type::SystemV:
+ case QNativeIpcKey::Type::PosixRealtime:
+ case QNativeIpcKey::Type::Windows:
+ return QString::fromRawData(typeString.constData(), typeString.size());
+ }
+
+ int value = int(type);
+ if (value >= 1 && value <= 0xff) {
+ // System V key with id different from 'Q'
+ typeString = staticTypeToString(QNativeIpcKey::Type::SystemV);
+ return typeString + QString::number(-value); // negative so it prepends a dash
+ }
+
+ return QString(); // invalid!
+}
+
+static QNativeIpcKey::Type stringToType(QStringView typeString)
+{
+ if (typeString == staticTypeToString(QNativeIpcKey::Type::PosixRealtime))
+ return QNativeIpcKey::Type::PosixRealtime;
+ if (typeString == staticTypeToString(QNativeIpcKey::Type::Windows))
+ return QNativeIpcKey::Type::Windows;
+
+ auto fromNumber = [](QStringView number, int low, int high) {
+ bool ok;
+ int n = -number.toInt(&ok, 10);
+ if (!ok || n < low || n > high)
+ return QNativeIpcKey::Type{};
+ return QNativeIpcKey::Type(n);
+ };
+
+ QStringView sysv = staticTypeToString(QNativeIpcKey::Type::SystemV);
+ if (typeString.startsWith(sysv)) {
+ if (typeString.size() == sysv.size())
+ return QNativeIpcKey::Type::SystemV;
+ return fromNumber(typeString.sliced(sysv.size()), 1, 0xff);
+ }
+
+ // invalid!
+ return QNativeIpcKey::Type{};
+}
+
+/*!
+ \internal
+
+ Legacy: this exists for compatibility with QSharedMemory and
+ QSystemSemaphore between 4.4 and 6.6.
+
+ Returns a QNativeIpcKey that contains a platform-safe key using rules
+ similar to QtIpcCommon::platformSafeKey() below, but using an algorithm
+ that is compatible with Qt 4.4 to 6.6. Additionally, the returned
+ QNativeIpcKey will record the input \a key so it can be included in the
+ string form if necessary to pass to other processes.
+*/
+QNativeIpcKey QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType,
+ QNativeIpcKey::Type type)
+{
+ QNativeIpcKey k(type);
+ if (key.isEmpty())
+ return k;
+
+ QByteArray hex = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Sha1).toHex();
+
+ if (type == QNativeIpcKey::Type::PosixRealtime) {
+#if defined(Q_OS_DARWIN)
+ if (qt_apple_isSandboxed()) {
+ // Sandboxed applications on Apple platforms require the shared memory name
+ // to be in the form <application group identifier>/<custom identifier>.
+ // Since we don't know which application group identifier the user wants
+ // to apply, we instead document that requirement, and use the key directly.
+ QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, key, key);
+ } else {
+ // The shared memory name limit on Apple platforms is very low (30 characters),
+ // so we can't use the logic below of combining the prefix, key, and a hash,
+ // to ensure a unique and valid name. Instead we use the first part of the
+ // hash, which should still long enough to avoid collisions in practice.
+ QString native = u'/' + QLatin1StringView(hex).left(SHM_NAME_MAX - 1);
+ QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, native, key);
+ }
+ return k;
+#endif
+ }
+
+ QString result;
+ result.reserve(1 + 18 + key.size() + 40);
+ switch (ipcType) {
+ case IpcType::SharedMemory:
+ result += "qipc_sharedmemory_"_L1;
+ break;
+ case IpcType::SystemSemaphore:
+ result += "qipc_systemsem_"_L1;
+ break;
+ }
+
+ for (QChar ch : key) {
+ if ((ch >= u'a' && ch <= u'z') ||
+ (ch >= u'A' && ch <= u'Z'))
+ result += ch;
+ }
+ result.append(QLatin1StringView(hex));
+
+ switch (type) {
+ case QNativeIpcKey::Type::Windows:
+ if (isIpcSupported(ipcType, QNativeIpcKey::Type::Windows))
+ QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key);
+ return k;
+ case QNativeIpcKey::Type::PosixRealtime:
+ result.prepend(u'/');
+ if (isIpcSupported(ipcType, QNativeIpcKey::Type::PosixRealtime))
+ QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key);
+ return k;
+ case QNativeIpcKey::Type::SystemV:
+ break;
+ }
+ if (isIpcSupported(ipcType, QNativeIpcKey::Type::SystemV)) {
+ result = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + u'/' + result;
+ QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, result, key);
+ }
+ return k;
+}
+
+/*!
+ \internal
+ Returns a QNativeIpcKey of type \a type, suitable for QSystemSemaphore or
+ QSharedMemory depending on \a ipcType. The returned native key is generated
+ from the Unicode input \a key and is safe for use on for the key type in
+ question in the current OS.
+*/
+QNativeIpcKey QtIpcCommon::platformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType,
+ QNativeIpcKey::Type type)
+{
+ QNativeIpcKey k(type);
+ if (key.isEmpty())
+ return k;
+
+ switch (type) {
+ case QNativeIpcKey::Type::PosixRealtime:
+ if (isIpcSupported(ipcType, QNativeIpcKey::Type::PosixRealtime)) {
+#ifdef SHM_NAME_MAX
+ // The shared memory name limit on Apple platforms is very low (30
+ // characters), so we have to cut it down to avoid ENAMETOOLONG. We
+ // hope that there won't be too many collisions...
+ k.setNativeKey(u'/' + QStringView(key).left(SHM_NAME_MAX - 1));
+#else
+ k.setNativeKey(u'/' + key);
+#endif
+ }
+ return k;
+
+ case QNativeIpcKey::Type::Windows:
+ if (isIpcSupported(ipcType, QNativeIpcKey::Type::Windows)) {
+ QStringView prefix;
+ QStringView payload = key;
+ // see https://learn.microsoft.com/en-us/windows/win32/termserv/kernel-object-namespaces
+ for (QStringView candidate : { u"Local\\", u"Global\\" }) {
+ if (!key.startsWith(candidate))
+ continue;
+ prefix = candidate;
+ payload = payload.sliced(prefix.size());
+ break;
+ }
+
+ QStringView mid;
+ switch (ipcType) {
+ case IpcType::SharedMemory: mid = u"shm_"; break;
+ case IpcType::SystemSemaphore: mid = u"sem_"; break;
+ }
+
+ QString result = prefix + mid + payload;
+#ifdef Q_OS_WINDOWS
+ result.truncate(MAX_PATH);
+#endif
+ k.setNativeKey(result);
+ }
+ return k;
+
+ case QNativeIpcKey::Type::SystemV:
+ break;
+ }
+
+ // System V
+ if (isIpcSupported(ipcType, QNativeIpcKey::Type::SystemV)) {
+ if (key.startsWith(u'/')) {
+ k.setNativeKey(key);
+ } else {
+ QString baseDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
+ k.setNativeKey(baseDir + u'/' + key);
+ }
+ }
+ return k;
+}
+
+/*!
+ \class QNativeIpcKey
+ \inmodule QtCore
+ \since 6.6
+ \brief The QNativeIpcKey class holds a native key used by QSystemSemaphore and QSharedMemory.
+
+ \compares equality
+
+ The \l QSharedMemory and \l QSystemSemaphore classes identify their
+ resource using a system-wide identifier known as a "key". The low-level key
+ value as well as the key type are encapsulated in Qt using the \l
+ QNativeIpcKey class.
+
+ Those two classes also provide the means to create native keys from a
+ cross-platform identifier, using QSharedMemory::platformSafeKey() and
+ QSystemSemaphore::platformSafeKey(). Applications should never share the
+ input to those functions, as different versions of Qt may perform different
+ transformations, resulting in different native keys. Instead, the
+ application that created the IPC object should communicate the resulting
+ native key using the methods described below.
+
+ For details on the key types, platform-specific limitations, and
+ interoperability with older or non-Qt applications, see the \l{Native IPC
+ Keys} documentation. That includes important information for sandboxed
+ applications on Apple platforms, including all apps obtained via the Apple
+ App Store.
+
+ \section1 Communicating keys to other processes
+ \section2 Communicating keys to other Qt processes
+
+ If the other process supports QNativeIpcKey, the best way of communicating
+ is via the string representation obtained from toString() and parsing it
+ using fromString(). This representation can be stored on a file whose name
+ is well-known or passed on the command-line to a child process using
+ QProcess::setArguments().
+
+ If the other process does not support QNativeIpcKey, then the two processes
+ can exchange the nativeKey() but the older code is likely unable to adjust
+ its key type. The legacyDefaultTypeForOs() function returns the type that
+ legacy code used, which may not match the \l{DefaultTypeForOs} constant.
+ This is still true even if the old application is not using the same build
+ as the new one (for example, it is a Qt 5 application), provided the
+ options passed to the Qt configure script are the same.
+
+ \section2 Communicating keys to non-Qt processes
+
+ When communicating with non-Qt processes, the application must arrange to
+ obtain the key type the other process is using. This is important
+ particularly on Unix systems, where both \l PosixRealtime and \l SystemV
+ are common.
+
+ \section1 String representation of native keys
+
+ The format of the string representation of a QNativeIpcKey is meant to be
+ stable and therefore backwards and forwards compatible, provided the key
+ type is supported by the Qt version in question. That is to say, an older
+ Qt will fail to parse the string representation of a key type introduced
+ after it was released. However, successfully parsing a string
+ representation does not imply the Qt classes can successfully create an
+ object of that type; applications should verify support using
+ QSharedMemory::isKeyTypeSupported() and QSystemSemaphore::isKeyTypeSupported().
+
+ The format of the string representation is formed by two components,
+ separated by a colon (':'). The first component is the key type, described
+ in the table below. The second component is a type-specific payload, using
+ \l{QByteArray::fromPercentEncoding}{percent-encoding}. For all currently
+ supported key types, the decoded form is identical to the contents of the
+ nativeKey() field.
+
+ \table
+ \row \li Key type \li String representation
+ \row \li \l PosixRealtime \li \c "posix"
+ \row \li \l SystemV \li \c "systemv"
+ \row \li \l Windows \li \c "windows"
+ \row \li Non-standard SystemV \li \c "systemv-" followed by a decimal number
+ \endtable
+
+ This format resembles a URI and allows parsing using URI/URL-parsing
+ functions, such as \l QUrl. When parsed by such API, the key type will show
+ up as the \l{QUrl::scheme()}{scheme}, and the payload will be the
+ \l{QUrl::path()}{path}. Use of query or fragments is reserved.
+
+ \sa QSharedMemory, QSystemSemaphore
+*/
+
+/*!
+ \enum QNativeIpcKey::Type
+
+ This enum describes the backend type for the IPC object. For details on the
+ key types, see the \l{Native IPC Keys} documentation.
+
+ \value SystemV X/Open System Initiative (XSI) or System V (SVr4) API
+ \value PosixRealtime IEEE 1003.1b (POSIX.1b) API
+ \value Windows Win32 API
+
+ \sa setType(), type()
+*/
+
+/*!
+ \variable QNativeIpcKey::DefaultTypeForOs
+
+ This constant expression variable holds the default native IPC type for the
+ current OS. It will be Type::Windows for Windows systems and
+ Type::PosixRealtime elsewhere. Note that this constant is different from
+ what \l QSharedMemory and \l QSystemSemaphore defaulted to on the majority
+ of Unix systems prior to Qt 6.6; see legacyDefaultTypeForOs() for more
+ information.
+*/
+
+/*!
+ \fn QNativeIpcKey::legacyDefaultTypeForOs() noexcept
+
+ Returns the \l{Type} that corresponds to the native IPC key that
+ \l{QSharedMemory} and \l{QSystemSemaphore} used to use prior to Qt 6.6.
+ Applications and libraries that must retain compatibility with code using
+ either class that was compiled with Qt prior to version 6.6 can use this
+ function to determine what IPC type the other applications may be using.
+
+ Note that this function relies on Qt having been built with identical
+ configure-time options.
+*/
+#if defined(Q_OS_DARWIN)
+QNativeIpcKey::Type QNativeIpcKey::defaultTypeForOs_internal() noexcept
+{
+ if (qt_apple_isSandboxed())
+ return Type::PosixRealtime;
+ return Type::SystemV;
+}
+#endif
+
+/*!
+ \fn QNativeIpcKey::QNativeIpcKey() noexcept
+
+ Constructs a QNativeIpcKey object of type \l DefaultTypeForOs with an empty key.
+*/
+
+/*!
+ \fn QNativeIpcKey::QNativeIpcKey(Type type) noexcept
+ \fn QNativeIpcKey::QNativeIpcKey(const QString &key, Type type)
+
+ Constructs a QNativeIpcKey object holding native key \a key (or empty on
+ the overload without the parameter) for type \a type.
+*/
+
+/*!
+ \fn QNativeIpcKey::QNativeIpcKey(const QNativeIpcKey &other)
+ \fn QNativeIpcKey::QNativeIpcKey(QNativeIpcKey &&other) noexcept
+ \fn QNativeIpcKey &QNativeIpcKey::operator=(const QNativeIpcKey &other)
+ \fn QNativeIpcKey &QNativeIpcKey::operator=(QNativeIpcKey &&other) noexcept
+
+ Copies or moves the content of \a other.
+*/
+void QNativeIpcKey::copy_internal(const QNativeIpcKey &other)
+{
+ d = new QNativeIpcKeyPrivate(*other.d);
+}
+
+void QNativeIpcKey::move_internal(QNativeIpcKey &&) noexcept
+{
+ // inline code already moved properly, nothing for us to do here
+}
+
+QNativeIpcKey &QNativeIpcKey::assign_internal(const QNativeIpcKey &other)
+{
+ Q_ASSERT(d || other.d); // only 3 cases to handle
+ if (d && !other.d)
+ *d = {};
+ else if (d)
+ *d = *other.d;
+ else
+ d = new QNativeIpcKeyPrivate(*other.d);
+ return *this;
+}
+
+/*!
+ \fn QNativeIpcKey::~QNativeIpcKey()
+
+ Disposes of this QNativeIpcKey object.
+*/
+void QNativeIpcKey::destroy_internal() noexcept
+{
+ delete d;
+}
+
+/*!
+ \fn QNativeIpcKey::swap(QNativeIpcKey &other) noexcept
+
+ Swaps the native IPC key and type \a other with this object.
+ This operation is very fast and never fails.
+*/
+
+/*!
+ \fn swap(QNativeIpcKey &value1, QNativeIpcKey &value2) noexcept
+ \relates QNativeIpcKey
+
+ Swaps the native IPC key and type \a value1 with \a value2.
+ This operation is very fast and never fails.
+*/
+
+/*!
+ \fn QNativeIpcKey::isEmpty() const
+
+ Returns true if the nativeKey() is empty.
+
+ \sa nativeKey()
+*/
+
+/*!
+ \fn QNativeIpcKey::isValid() const
+
+ Returns true if this object contains a valid native IPC key type. Invalid
+ types are usually the result of a failure to parse a string representation
+ using fromString().
+
+ This function performs no check on the whether the key string is actually
+ supported or valid for the current operating system.
+
+ \sa type(), fromString()
+*/
+
+/*!
+ \fn QNativeIpcKey::type() const noexcept
+
+ Returns the key type associated with this object.
+
+ \sa nativeKey(), setType()
+*/
+
+/*!
+ \fn QNativeIpcKey::setType(Type type)
+
+ Sets the IPC type of this object to \a type.
+
+ \sa type(), setNativeKey()
+*/
+void QNativeIpcKey::setType_internal(Type type)
+{
+ Q_UNUSED(type);
+}
+
+/*!
+ \fn QNativeIpcKey::nativeKey() const noexcept
+
+ Returns the native key string associated with this object.
+
+ \sa setNativeKey(), type()
+*/
+
+/*!
+ \fn QNativeIpcKey::setNativeKey(const QString &newKey)
+
+ Sets the native key for this object to \a newKey.
+
+ \sa nativeKey(), setType()
+*/
+void QNativeIpcKey::setNativeKey_internal(const QString &)
+{
+ d->legacyKey_.clear();
+}
+
+/*!
+ \fn size_t QNativeIpcKey::qHash(const QNativeIpcKey &ipcKey) noexcept
+
+ Returns the hash value for \a ipcKey, using a default seed of \c 0.
+*/
+
+/*!
+ \fn size_t QNativeIpcKey::qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept
+
+ Returns the hash value for \a ipcKey, using \a seed to seed the calculation.
+*/
+size_t qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept
+{
+ // by *choice*, we're not including d->legacyKey_ in the hash -- it's
+ // already partially encoded in the key
+ return qHashMulti(seed, ipcKey.key, ipcKey.type());
+}
+
+/*!
+ \fn bool QNativeIpcKey::operator==(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
+ \fn bool QNativeIpcKey::operator!=(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
+
+ Returns true if the \a lhs and \a rhs objects hold the same (or different) contents.
+*/
+int QNativeIpcKey::compare_internal(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept
+{
+ return (QNativeIpcKeyPrivate::legacyKey(lhs) == QNativeIpcKeyPrivate::legacyKey(rhs)) ? 0 : 1;
+}
+
+/*!
+ Returns the string representation of this object. String representations
+ are useful to inform other processes of the key this process created and
+ that they should attach to.
+
+ This function returns a null string if the current object is
+ \l{isValid()}{invalid}.
+
+ \sa fromString()
+*/
+QString QNativeIpcKey::toString() const
+{
+ QString prefix = typeToString(type());
+ if (prefix.isEmpty()) {
+ Q_ASSERT(prefix.isNull());
+ return prefix;
+ }
+
+ QString copy = nativeKey();
+ copy.replace(u'%', "%25"_L1);
+ if (copy.startsWith("//"_L1))
+ copy.replace(0, 2, u"/%2F"_s); // ensure it's parsed as a URL path
+
+ QUrl u;
+ u.setScheme(prefix);
+ u.setPath(copy, QUrl::TolerantMode);
+ if (isSlowPath()) {
+ QUrlQuery q;
+ if (!d->legacyKey_.isEmpty())
+ q.addQueryItem(u"legacyKey"_s, QString(d->legacyKey_).replace(u'%', "%25"_L1));
+ u.setQuery(q);
+ }
+ return u.toString(QUrl::DecodeReserved);
+}
+
+/*!
+ Parses the string form \a text and returns the corresponding QNativeIpcKey.
+ String representations are useful to inform other processes of the key this
+ process created and they should attach to.
+
+ If the string could not be parsed, this function returns an
+ \l{isValid()}{invalid} object.
+
+ \sa toString(), isValid()
+*/
+QNativeIpcKey QNativeIpcKey::fromString(const QString &text)
+{
+ QUrl u(text, QUrl::TolerantMode);
+ Type invalidType = {};
+ Type type = stringToType(u.scheme());
+ if (type == invalidType || !u.isValid() || !u.userInfo().isEmpty() || !u.host().isEmpty()
+ || u.port() != -1)
+ return QNativeIpcKey(invalidType);
+
+ QNativeIpcKey result(QString(), type);
+ if (result.type() != type) // range check, just in case
+ return QNativeIpcKey(invalidType);
+
+ // decode the payload
+ result.setNativeKey(u.path());
+
+ if (u.hasQuery()) {
+ const QList items = QUrlQuery(u).queryItems();
+ for (const auto &item : items) {
+ if (item.first == u"legacyKey"_s) {
+ QString legacyKey = QUrl::fromPercentEncoding(item.second.toUtf8());
+ QNativeIpcKeyPrivate::setLegacyKey(result, std::move(legacyKey));
+ } else {
+ // unknown query item
+ return QNativeIpcKey(invalidType);
+ }
+ }
+ }
+ return result;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qtipccommon.cpp"
+
+#endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)