diff options
Diffstat (limited to 'src/corelib/ipc/qsharedmemory_systemv.cpp')
-rw-r--r-- | src/corelib/ipc/qsharedmemory_systemv.cpp | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/src/corelib/ipc/qsharedmemory_systemv.cpp b/src/corelib/ipc/qsharedmemory_systemv.cpp new file mode 100644 index 0000000000..dc9de11091 --- /dev/null +++ b/src/corelib/ipc/qsharedmemory_systemv.cpp @@ -0,0 +1,212 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qsharedmemory.h" +#include "qsharedmemory_p.h" + +#include "qtipccommon_p.h" + +#include <qdir.h> +#include <qdebug.h> + +#include <errno.h> + +#if QT_CONFIG(sharedmemory) +#if QT_CONFIG(sysv_shm) +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/mman.h> +#include <sys/shm.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#include "private/qcore_unix_p.h" +#if defined(Q_OS_DARWIN) +#include "private/qcore_mac_p.h" +#endif + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; +using namespace QtIpcCommon; + +bool QSharedMemorySystemV::runtimeSupportCheck() +{ +#if defined(Q_OS_DARWIN) + if (qt_apple_isSandboxed()) + return false; +#endif + static const bool result = []() { + (void)shmget(IPC_PRIVATE, ~size_t(0), 0); // this will fail + return errno != ENOSYS; + }(); + return result; +} + + +inline void QSharedMemorySystemV::updateNativeKeyFile(const QNativeIpcKey &nativeKey) +{ + Q_ASSERT(nativeKeyFile.isEmpty() ); + if (!nativeKey.nativeKey().isEmpty()) + nativeKeyFile = QFile::encodeName(nativeKey.nativeKey()); +} + +/*! + \internal + + If not already made create the handle used for accessing the shared memory. +*/ +key_t QSharedMemorySystemV::handle(QSharedMemoryPrivate *self) +{ + // already made + if (unix_key) + return unix_key; + + // don't allow making handles on empty keys + if (nativeKeyFile.isEmpty()) + updateNativeKeyFile(self->nativeKey); + if (nativeKeyFile.isEmpty()) { + self->setError(QSharedMemory::KeyError, + QSharedMemory::tr("%1: key is empty") + .arg("QSharedMemory::handle:"_L1)); + return 0; + } + + unix_key = ftok(nativeKeyFile, int(self->nativeKey.type())); + if (unix_key < 0) { + self->setUnixErrorString("QSharedMemory::handle"_L1); + nativeKeyFile.clear(); + unix_key = 0; + } + return unix_key; +} + +bool QSharedMemorySystemV::cleanHandle(QSharedMemoryPrivate *self) +{ + if (unix_key == 0) + return true; + + // Get the number of current attachments + struct shmid_ds shmid_ds; + QByteArray keyfile = std::exchange(nativeKeyFile, QByteArray()); + + int id = shmget(unix_key, 0, 0400); + unix_key = 0; + if (shmctl(id, IPC_STAT, &shmid_ds)) + return errno != EINVAL; + + // If there are still attachments, keep the keep file and shm + if (shmid_ds.shm_nattch != 0) + return true; + + if (shmctl(id, IPC_RMID, &shmid_ds) < 0) { + if (errno != EINVAL) { + self->setUnixErrorString("QSharedMemory::remove"_L1); + return false; + } + }; + + // remove file + return unlink(keyfile) == 0; +} + +bool QSharedMemorySystemV::create(QSharedMemoryPrivate *self, qsizetype size) +{ + // build file if needed + bool createdFile = false; + updateNativeKeyFile(self->nativeKey); + int built = createUnixKeyFile(nativeKeyFile); + if (built == -1) { + self->setError(QSharedMemory::KeyError, + QSharedMemory::tr("%1: unable to make key") + .arg("QSharedMemory::handle:"_L1)); + return false; + } + if (built == 1) { + createdFile = true; + } + + // get handle + if (!handle(self)) { + if (createdFile) + unlink(nativeKeyFile); + return false; + } + + // create + if (-1 == shmget(unix_key, size_t(size), 0600 | IPC_CREAT | IPC_EXCL)) { + const auto function = "QSharedMemory::create"_L1; + switch (errno) { + case EINVAL: + self->setError(QSharedMemory::InvalidSize, + QSharedMemory::tr("%1: system-imposed size restrictions") + .arg("QSharedMemory::handle"_L1)); + break; + default: + self->setUnixErrorString(function); + } + if (createdFile && self->error != QSharedMemory::AlreadyExists) + unlink(nativeKeyFile); + return false; + } + + return true; +} + +bool QSharedMemorySystemV::attach(QSharedMemoryPrivate *self, QSharedMemory::AccessMode mode) +{ + // grab the shared memory segment id + int id = shmget(unix_key, 0, (mode == QSharedMemory::ReadOnly ? 0400 : 0600)); + if (-1 == id) { + self->setUnixErrorString("QSharedMemory::attach (shmget)"_L1); + unix_key = 0; + nativeKeyFile.clear(); + return false; + } + + // grab the memory + self->memory = shmat(id, nullptr, (mode == QSharedMemory::ReadOnly ? SHM_RDONLY : 0)); + if (self->memory == MAP_FAILED) { + self->memory = nullptr; + self->setUnixErrorString("QSharedMemory::attach (shmat)"_L1); + return false; + } + + // grab the size + shmid_ds shmid_ds; + if (!shmctl(id, IPC_STAT, &shmid_ds)) { + self->size = (qsizetype)shmid_ds.shm_segsz; + } else { + self->setUnixErrorString("QSharedMemory::attach (shmctl)"_L1); + return false; + } + + return true; +} + +bool QSharedMemorySystemV::detach(QSharedMemoryPrivate *self) +{ + // detach from the memory segment + if (shmdt(self->memory) < 0) { + const auto function = "QSharedMemory::detach"_L1; + switch (errno) { + case EINVAL: + self->setError(QSharedMemory::NotFound, + QSharedMemory::tr("%1: not attached").arg(function)); + break; + default: + self->setUnixErrorString(function); + } + return false; + } + self->memory = nullptr; + self->size = 0; + + return cleanHandle(self); +} + +QT_END_NAMESPACE + +#endif // QT_CONFIG(sysv_shm) +#endif // QT_CONFIG(sharedmemory) |