1// Copyright (C) 2016 The Qt Company Ltd.
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 "qsharedmemory.h"
5#include "qsharedmemory_p.h"
6
7#include "qtipccommon_p.h"
8
9#include <qdir.h>
10#include <qdebug.h>
11
12#include <errno.h>
13
14#if QT_CONFIG(sharedmemory)
15#if QT_CONFIG(sysv_shm)
16#include <sys/types.h>
17#include <sys/ipc.h>
18#include <sys/mman.h>
19#include <sys/shm.h>
20#include <sys/stat.h>
21#include <fcntl.h>
22#include <unistd.h>
23
24#include "private/qcore_unix_p.h"
25#if defined(Q_OS_DARWIN)
26#include "private/qcore_mac_p.h"
27#endif
28
29QT_BEGIN_NAMESPACE
30
31using namespace Qt::StringLiterals;
32using namespace QtIpcCommon;
33
34bool QSharedMemorySystemV::runtimeSupportCheck()
35{
36#if defined(Q_OS_DARWIN)
37 if (qt_apple_isSandboxed())
38 return false;
39#endif
40 static const bool result = []() {
41 (void)shmget(IPC_PRIVATE, size: ~size_t(0), shmflg: 0); // this will fail
42 return errno != ENOSYS;
43 }();
44 return result;
45}
46
47
48inline void QSharedMemorySystemV::updateNativeKeyFile(const QNativeIpcKey &nativeKey)
49{
50 Q_ASSERT(nativeKeyFile.isEmpty() );
51 if (!nativeKey.nativeKey().isEmpty())
52 nativeKeyFile = QFile::encodeName(fileName: nativeKey.nativeKey());
53}
54
55/*!
56 \internal
57
58 If not already made create the handle used for accessing the shared memory.
59*/
60key_t QSharedMemorySystemV::handle(QSharedMemoryPrivate *self)
61{
62 // already made
63 if (unix_key)
64 return unix_key;
65
66 // don't allow making handles on empty keys
67 if (nativeKeyFile.isEmpty())
68 updateNativeKeyFile(nativeKey: self->nativeKey);
69 if (nativeKeyFile.isEmpty()) {
70 self->setError(e: QSharedMemory::KeyError,
71 message: QSharedMemory::tr(s: "%1: key is empty")
72 .arg(a: "QSharedMemory::handle:"_L1));
73 return 0;
74 }
75
76 unix_key = ftok(pathname: nativeKeyFile, proj_id: int(self->nativeKey.type()));
77 if (unix_key < 0) {
78 self->setUnixErrorString("QSharedMemory::handle"_L1);
79 nativeKeyFile.clear();
80 unix_key = 0;
81 }
82 return unix_key;
83}
84
85bool QSharedMemorySystemV::cleanHandle(QSharedMemoryPrivate *self)
86{
87 if (unix_key == 0)
88 return true;
89
90 // Get the number of current attachments
91 struct shmid_ds shmid_ds;
92 QByteArray keyfile = std::exchange(obj&: nativeKeyFile, new_val: QByteArray());
93
94 int id = shmget(key: unix_key, size: 0, shmflg: 0400);
95 unix_key = 0;
96 if (shmctl(shmid: id, IPC_STAT, buf: &shmid_ds))
97 return errno != EINVAL;
98
99 // If there are still attachments, keep the keep file and shm
100 if (shmid_ds.shm_nattch != 0)
101 return true;
102
103 if (shmctl(shmid: id, IPC_RMID, buf: &shmid_ds) < 0) {
104 if (errno != EINVAL) {
105 self->setUnixErrorString("QSharedMemory::remove"_L1);
106 return false;
107 }
108 };
109
110 // remove file
111 return unlink(name: keyfile) == 0;
112}
113
114bool QSharedMemorySystemV::create(QSharedMemoryPrivate *self, qsizetype size)
115{
116 // build file if needed
117 bool createdFile = false;
118 updateNativeKeyFile(nativeKey: self->nativeKey);
119 int built = createUnixKeyFile(fileName: nativeKeyFile);
120 if (built == -1) {
121 self->setError(e: QSharedMemory::KeyError,
122 message: QSharedMemory::tr(s: "%1: unable to make key")
123 .arg(a: "QSharedMemory::handle:"_L1));
124 return false;
125 }
126 if (built == 1) {
127 createdFile = true;
128 }
129
130 // get handle
131 if (!handle(self)) {
132 if (createdFile)
133 unlink(name: nativeKeyFile);
134 return false;
135 }
136
137 // create
138 if (-1 == shmget(key: unix_key, size: size_t(size), shmflg: 0600 | IPC_CREAT | IPC_EXCL)) {
139 const auto function = "QSharedMemory::create"_L1;
140 switch (errno) {
141 case EINVAL:
142 self->setError(e: QSharedMemory::InvalidSize,
143 message: QSharedMemory::tr(s: "%1: system-imposed size restrictions")
144 .arg(a: "QSharedMemory::handle"_L1));
145 break;
146 default:
147 self->setUnixErrorString(function);
148 }
149 if (createdFile && self->error != QSharedMemory::AlreadyExists)
150 unlink(name: nativeKeyFile);
151 return false;
152 }
153
154 return true;
155}
156
157bool QSharedMemorySystemV::attach(QSharedMemoryPrivate *self, QSharedMemory::AccessMode mode)
158{
159 // grab the shared memory segment id
160 int id = shmget(key: unix_key, size: 0, shmflg: (mode == QSharedMemory::ReadOnly ? 0400 : 0600));
161 if (-1 == id) {
162 self->setUnixErrorString("QSharedMemory::attach (shmget)"_L1);
163 unix_key = 0;
164 nativeKeyFile.clear();
165 return false;
166 }
167
168 // grab the memory
169 self->memory = shmat(shmid: id, shmaddr: nullptr, shmflg: (mode == QSharedMemory::ReadOnly ? SHM_RDONLY : 0));
170 if (self->memory == MAP_FAILED) {
171 self->memory = nullptr;
172 self->setUnixErrorString("QSharedMemory::attach (shmat)"_L1);
173 return false;
174 }
175
176 // grab the size
177 shmid_ds shmid_ds;
178 if (!shmctl(shmid: id, IPC_STAT, buf: &shmid_ds)) {
179 self->size = (qsizetype)shmid_ds.shm_segsz;
180 } else {
181 self->setUnixErrorString("QSharedMemory::attach (shmctl)"_L1);
182 return false;
183 }
184
185 return true;
186}
187
188bool QSharedMemorySystemV::detach(QSharedMemoryPrivate *self)
189{
190 // detach from the memory segment
191 if (shmdt(shmaddr: self->memory) < 0) {
192 const auto function = "QSharedMemory::detach"_L1;
193 switch (errno) {
194 case EINVAL:
195 self->setError(e: QSharedMemory::NotFound,
196 message: QSharedMemory::tr(s: "%1: not attached").arg(a: function));
197 break;
198 default:
199 self->setUnixErrorString(function);
200 }
201 return false;
202 }
203 self->memory = nullptr;
204 self->size = 0;
205
206 return cleanHandle(self);
207}
208
209QT_END_NAMESPACE
210
211#endif // QT_CONFIG(sysv_shm)
212#endif // QT_CONFIG(sharedmemory)
213

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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