1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qplatformdefs.h"
41
42#include "qsharedmemory.h"
43#include "qsharedmemory_p.h"
44#include "qsystemsemaphore.h"
45#include <qdir.h>
46#include <qdebug.h>
47
48#include <errno.h>
49
50#ifndef QT_POSIX_IPC
51
52#ifndef QT_NO_SHAREDMEMORY
53#include <sys/types.h>
54#include <sys/ipc.h>
55#include <sys/shm.h>
56#include <sys/stat.h>
57#include <fcntl.h>
58#include <unistd.h>
59#endif //QT_NO_SHAREDMEMORY
60
61#include "private/qcore_unix_p.h"
62
63#ifndef QT_NO_SHAREDMEMORY
64QT_BEGIN_NAMESPACE
65
66/*!
67 \internal
68
69 If not already made create the handle used for accessing the shared memory.
70*/
71key_t QSharedMemoryPrivate::handle()
72{
73 // already made
74 if (unix_key)
75 return unix_key;
76
77 // don't allow making handles on empty keys
78 if (nativeKey.isEmpty()) {
79 errorString = QSharedMemory::tr("%1: key is empty").arg(QLatin1String("QSharedMemory::handle:"));
80 error = QSharedMemory::KeyError;
81 return 0;
82 }
83
84 // ftok requires that an actual file exists somewhere
85 if (!QFile::exists(nativeKey)) {
86 errorString = QSharedMemory::tr("%1: UNIX key file doesn't exist").arg(QLatin1String("QSharedMemory::handle:"));
87 error = QSharedMemory::NotFound;
88 return 0;
89 }
90
91 unix_key = ftok(QFile::encodeName(nativeKey).constData(), 'Q');
92 if (-1 == unix_key) {
93 errorString = QSharedMemory::tr("%1: ftok failed").arg(QLatin1String("QSharedMemory::handle:"));
94 error = QSharedMemory::KeyError;
95 unix_key = 0;
96 }
97 return unix_key;
98}
99
100#endif // QT_NO_SHAREDMEMORY
101
102#if !(defined(QT_NO_SHAREDMEMORY) && defined(QT_NO_SYSTEMSEMAPHORE))
103/*!
104 \internal
105 Creates the unix file if needed.
106 returns \c true if the unix file was created.
107
108 -1 error
109 0 already existed
110 1 created
111 */
112int QSharedMemoryPrivate::createUnixKeyFile(const QString &fileName)
113{
114 int fd = qt_safe_open(QFile::encodeName(fileName).constData(),
115 O_EXCL | O_CREAT | O_RDWR, 0640);
116 if (-1 == fd) {
117 if (errno == EEXIST)
118 return 0;
119 return -1;
120 } else {
121 close(fd);
122 }
123 return 1;
124}
125#endif // QT_NO_SHAREDMEMORY && QT_NO_SYSTEMSEMAPHORE
126
127#ifndef QT_NO_SHAREDMEMORY
128
129bool QSharedMemoryPrivate::cleanHandle()
130{
131 unix_key = 0;
132 return true;
133}
134
135bool QSharedMemoryPrivate::create(qsizetype size)
136{
137 // build file if needed
138 bool createdFile = false;
139 int built = createUnixKeyFile(nativeKey);
140 if (built == -1) {
141 errorString = QSharedMemory::tr("%1: unable to make key").arg(QLatin1String("QSharedMemory::handle:"));
142 error = QSharedMemory::KeyError;
143 return false;
144 }
145 if (built == 1) {
146 createdFile = true;
147 }
148
149 // get handle
150 if (!handle()) {
151 if (createdFile)
152 QFile::remove(nativeKey);
153 return false;
154 }
155
156 // create
157 if (-1 == shmget(unix_key, size_t(size), 0600 | IPC_CREAT | IPC_EXCL)) {
158 const QLatin1String function("QSharedMemory::create");
159 switch (errno) {
160 case EINVAL:
161 errorString = QSharedMemory::tr("%1: system-imposed size restrictions").arg(QLatin1String("QSharedMemory::handle"));
162 error = QSharedMemory::InvalidSize;
163 break;
164 default:
165 setErrorString(function);
166 }
167 if (createdFile && error != QSharedMemory::AlreadyExists)
168 QFile::remove(nativeKey);
169 return false;
170 }
171
172 return true;
173}
174
175bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode)
176{
177 // grab the shared memory segment id
178 int id = shmget(unix_key, 0, (mode == QSharedMemory::ReadOnly ? 0400 : 0600));
179 if (-1 == id) {
180 setErrorString(QLatin1String("QSharedMemory::attach (shmget)"));
181 return false;
182 }
183
184 // grab the memory
185 memory = shmat(id, nullptr, (mode == QSharedMemory::ReadOnly ? SHM_RDONLY : 0));
186 if ((void *)-1 == memory) {
187 memory = nullptr;
188 setErrorString(QLatin1String("QSharedMemory::attach (shmat)"));
189 return false;
190 }
191
192 // grab the size
193 shmid_ds shmid_ds;
194 if (!shmctl(id, IPC_STAT, &shmid_ds)) {
195 size = (qsizetype)shmid_ds.shm_segsz;
196 } else {
197 setErrorString(QLatin1String("QSharedMemory::attach (shmctl)"));
198 return false;
199 }
200
201 return true;
202}
203
204bool QSharedMemoryPrivate::detach()
205{
206 // detach from the memory segment
207 if (-1 == shmdt(memory)) {
208 const QLatin1String function("QSharedMemory::detach");
209 switch (errno) {
210 case EINVAL:
211 errorString = QSharedMemory::tr("%1: not attached").arg(function);
212 error = QSharedMemory::NotFound;
213 break;
214 default:
215 setErrorString(function);
216 }
217 return false;
218 }
219 memory = nullptr;
220 size = 0;
221
222 // Get the number of current attachments
223 int id = shmget(unix_key, 0, 0400);
224 cleanHandle();
225
226 struct shmid_ds shmid_ds;
227 if (0 != shmctl(id, IPC_STAT, &shmid_ds)) {
228 switch (errno) {
229 case EINVAL:
230 return true;
231 default:
232 return false;
233 }
234 }
235 // If there are no attachments then remove it.
236 if (shmid_ds.shm_nattch == 0) {
237 // mark for removal
238 if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) {
239 setErrorString(QLatin1String("QSharedMemory::remove"));
240 switch (errno) {
241 case EINVAL:
242 return true;
243 default:
244 return false;
245 }
246 }
247
248 // remove file
249 if (!QFile::remove(nativeKey))
250 return false;
251 }
252 return true;
253}
254
255
256QT_END_NAMESPACE
257
258#endif // QT_NO_SHAREDMEMORY
259
260#endif // QT_POSIX_IPC
261