summaryrefslogtreecommitdiffstats
path: root/src/corelib/ipc/qsharedmemory_systemv.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/ipc/qsharedmemory_systemv.cpp')
-rw-r--r--src/corelib/ipc/qsharedmemory_systemv.cpp212
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)