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 |
64 | QT_BEGIN_NAMESPACE |
65 | |
66 | /*! |
67 | \internal |
68 | |
69 | If not already made create the handle used for accessing the shared memory. |
70 | */ |
71 | key_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(s: "%1: key is empty" ).arg(a: 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(fileName: nativeKey)) { |
86 | errorString = QSharedMemory::tr(s: "%1: UNIX key file doesn't exist" ).arg(a: QLatin1String("QSharedMemory::handle:" )); |
87 | error = QSharedMemory::NotFound; |
88 | return 0; |
89 | } |
90 | |
91 | unix_key = ftok(pathname: QFile::encodeName(fileName: nativeKey).constData(), proj_id: 'Q'); |
92 | if (-1 == unix_key) { |
93 | errorString = QSharedMemory::tr(s: "%1: ftok failed" ).arg(a: 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 | */ |
112 | int QSharedMemoryPrivate::createUnixKeyFile(const QString &fileName) |
113 | { |
114 | int fd = qt_safe_open(pathname: QFile::encodeName(fileName).constData(), |
115 | O_EXCL | O_CREAT | O_RDWR, mode: 0640); |
116 | if (-1 == fd) { |
117 | if (errno == EEXIST) |
118 | return 0; |
119 | return -1; |
120 | } else { |
121 | close(fd: fd); |
122 | } |
123 | return 1; |
124 | } |
125 | #endif // QT_NO_SHAREDMEMORY && QT_NO_SYSTEMSEMAPHORE |
126 | |
127 | #ifndef QT_NO_SHAREDMEMORY |
128 | |
129 | bool QSharedMemoryPrivate::cleanHandle() |
130 | { |
131 | unix_key = 0; |
132 | return true; |
133 | } |
134 | |
135 | bool QSharedMemoryPrivate::create(int size) |
136 | { |
137 | // build file if needed |
138 | bool createdFile = false; |
139 | int built = createUnixKeyFile(fileName: nativeKey); |
140 | if (built == -1) { |
141 | errorString = QSharedMemory::tr(s: "%1: unable to make key" ).arg(a: 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(fileName: nativeKey); |
153 | return false; |
154 | } |
155 | |
156 | // create |
157 | if (-1 == shmget(key: unix_key, size: size, shmflg: 0600 | IPC_CREAT | IPC_EXCL)) { |
158 | const QLatin1String function("QSharedMemory::create" ); |
159 | switch (errno) { |
160 | case EINVAL: |
161 | errorString = QSharedMemory::tr(s: "%1: system-imposed size restrictions" ).arg(a: QLatin1String("QSharedMemory::handle" )); |
162 | error = QSharedMemory::InvalidSize; |
163 | break; |
164 | default: |
165 | setErrorString(function); |
166 | } |
167 | if (createdFile && error != QSharedMemory::AlreadyExists) |
168 | QFile::remove(fileName: nativeKey); |
169 | return false; |
170 | } |
171 | |
172 | return true; |
173 | } |
174 | |
175 | bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode) |
176 | { |
177 | // grab the shared memory segment id |
178 | int id = shmget(key: unix_key, size: 0, shmflg: (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(shmid: id, shmaddr: nullptr, shmflg: (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(shmid: id, IPC_STAT, buf: &shmid_ds)) { |
195 | size = (int)shmid_ds.shm_segsz; |
196 | } else { |
197 | setErrorString(QLatin1String("QSharedMemory::attach (shmctl)" )); |
198 | return false; |
199 | } |
200 | |
201 | return true; |
202 | } |
203 | |
204 | bool QSharedMemoryPrivate::detach() |
205 | { |
206 | // detach from the memory segment |
207 | if (-1 == shmdt(shmaddr: memory)) { |
208 | const QLatin1String function("QSharedMemory::detach" ); |
209 | switch (errno) { |
210 | case EINVAL: |
211 | errorString = QSharedMemory::tr(s: "%1: not attached" ).arg(a: 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(key: unix_key, size: 0, shmflg: 0400); |
224 | cleanHandle(); |
225 | |
226 | struct shmid_ds shmid_ds; |
227 | if (0 != shmctl(shmid: id, IPC_STAT, buf: &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(shmid: id, IPC_RMID, buf: &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(fileName: nativeKey)) |
250 | return false; |
251 | } |
252 | return true; |
253 | } |
254 | |
255 | |
256 | QT_END_NAMESPACE |
257 | |
258 | #endif // QT_NO_SHAREDMEMORY |
259 | |
260 | #endif // QT_POSIX_IPC |
261 | |