Qt 4.8
qsharedmemory_unix.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qplatformdefs.h"
43 
44 #include "qsharedmemory.h"
45 #include "qsharedmemory_p.h"
46 
47 #include <qdebug.h>
48 #include <qfile.h>
49 
50 #ifndef QT_NO_SHAREDMEMORY
51 #include <sys/types.h>
52 #include <sys/ipc.h>
53 #ifndef QT_POSIX_IPC
54 #include <sys/shm.h>
55 #else
56 #include <sys/mman.h>
57 #include <sys/stat.h>
58 #endif
59 #include <fcntl.h>
60 #include <unistd.h>
61 #endif // QT_NO_SHAREDMEMORY
62 #include <errno.h>
63 
64 #include "private/qcore_unix_p.h"
65 
66 #ifndef QT_NO_SHAREDMEMORY
67 
68 //#define QSHAREDMEMORY_DEBUG
69 
71 
73  : QObjectPrivate(), memory(0), size(0), error(QSharedMemory::NoError),
75  systemSemaphore(QString()), lockedByMe(false),
76 #endif
77 #ifndef QT_POSIX_IPC
78  unix_key(0)
79 #else
80  hand(0)
81 #endif
82 {
83 }
84 
86 {
87  // EINVAL is handled in functions so they can give better error strings
88  switch (errno) {
89  case EACCES:
90  case EPERM:
91  errorString = QSharedMemory::tr("%1: permission denied").arg(function);
93  break;
94  case EEXIST:
95  errorString = QSharedMemory::tr("%1: already exists").arg(function);
97  break;
98  case ENOENT:
99  errorString = QSharedMemory::tr("%1: doesn't exist").arg(function);
101  break;
102  case EAGAIN:
103  case EMFILE:
104  case ENFILE:
105  case ENOMEM:
106  case ENOSPC:
107  errorString = QSharedMemory::tr("%1: out of resources").arg(function);
109  break;
110  case EOVERFLOW:
111  errorString = QSharedMemory::tr("%1: invalid size").arg(function);
113  break;
114  default:
115  errorString = QSharedMemory::tr("%1: unknown error %2").arg(function).arg(errno);
117 #ifdef QSHAREDMEMORY_DEBUG
118  qDebug() << errorString << "key" << key << "errno" << errno << EINVAL;
119 #endif
120  break;
121  }
122 }
123 
132 #ifndef QT_POSIX_IPC
134 {
135  // already made
136  if (unix_key)
137  return unix_key;
138 
139  // don't allow making handles on empty keys
140  if (nativeKey.isEmpty()) {
141  errorString = QSharedMemory::tr("%1: key is empty").arg(QLatin1String("QSharedMemory::handle"));
143  return 0;
144  }
145 
146  // ftok requires that an actual file exists somewhere
147  if (!QFile::exists(nativeKey)) {
148  errorString = QSharedMemory::tr("%1: UNIX key file doesn't exist").arg(QLatin1String("QSharedMemory::handle"));
150  return 0;
151  }
152 
153  unix_key = ftok(QFile::encodeName(nativeKey).constData(), 'Q');
154  if (-1 == unix_key) {
155  errorString = QSharedMemory::tr("%1: ftok failed").arg(QLatin1String("QSharedMemory::handle"));
157  unix_key = 0;
158  }
159  return unix_key;
160 }
161 #else
163 {
164  // don't allow making handles on empty keys
165  QString safeKey = makePlatformSafeKey(key);
166  if (safeKey.isEmpty()) {
167  errorString = QSharedMemory::tr("%1: key is empty").arg(QLatin1String("QSharedMemory::handle"));
169  return 0;
170  }
171 
172  return 1;
173 }
174 #endif // QT_POSIX_IPC
175 
176 #endif // QT_NO_SHAREDMEMORY
177 
178 #if !(defined(QT_NO_SHAREDMEMORY) && defined(QT_NO_SYSTEMSEMAPHORE))
179 
192 {
193 #ifndef QT_POSIX_IPC
194  if (QFile::exists(fileName))
195  return 0;
196 
197  int fd = qt_safe_open(QFile::encodeName(fileName).constData(),
198  O_EXCL | O_CREAT | O_RDWR, 0640);
199  if (-1 == fd) {
200  if (errno == EEXIST)
201  return 0;
202  return -1;
203  } else {
204  qt_safe_close(fd);
205  }
206  return 1;
207 #else
208  Q_UNUSED(fileName);
209  // nothing to do
210  return -1;
211 #endif
212 }
213 #endif // QT_NO_SHAREDMEMORY && QT_NO_SYSTEMSEMAPHORE
214 
215 #ifndef QT_NO_SHAREDMEMORY
216 
218 {
219 #ifndef QT_POSIX_IPC
220  unix_key = 0;
221 #else
223  hand = 0;
224 #endif
225 }
226 
228 {
229 #ifndef QT_POSIX_IPC
230  // build file if needed
231  int built = createUnixKeyFile(nativeKey);
232  if (built == -1) {
233  errorString = QSharedMemory::tr("%1: unable to make key").arg(QLatin1String("QSharedMemory::create"));
235  return false;
236  }
237  bool createdFile = built == 1;
238 
239  // get handle
240  if (!handle()) {
241  if (createdFile)
243  return false;
244  }
245 
246  // create
247  if (-1 == shmget(unix_key, size, 0600 | IPC_CREAT | IPC_EXCL)) {
248  QString function = QLatin1String("QSharedMemory::create");
249  switch (errno) {
250  case EINVAL:
251  errorString = QSharedMemory::tr("%1: system-imposed size restrictions").arg(function);
253  break;
254  default:
255  setErrorString(function);
256  }
257  if (createdFile && error != QSharedMemory::AlreadyExists)
259  return false;
260  }
261 #else
262  if (!handle())
263  return false;
264 
266 
267  int fd;
268  EINTR_LOOP(fd, shm_open(shmName.constData(), O_RDWR | O_CREAT | O_EXCL, 0666));
269  if (fd == -1) {
270  QString function = QLatin1String("QSharedMemory::create");
271  switch (errno) {
272  case ENAMETOOLONG:
273  case EINVAL:
274  errorString = QSharedMemory::tr("%1: bad name").arg(function);
276  break;
277  default:
278  setErrorString(function);
279  }
280  return false;
281  }
282 
283  // the size may only be set once; ignore errors
284  int ret;
285  EINTR_LOOP(ret, ftruncate(fd, size));
286  if (ret == -1) {
287  setErrorString(QLatin1String("QSharedMemory::create (ftruncate)"));
288  qt_safe_close(fd);
289  return false;
290  }
291 
292  qt_safe_close(fd);
293 #endif // QT_POSIX_IPC
294 
295  return true;
296 }
297 
299 {
300 #ifndef QT_POSIX_IPC
301  // grab the shared memory segment id
302  int id = shmget(unix_key, 0, (mode == QSharedMemory::ReadOnly ? 0400 : 0600));
303  if (-1 == id) {
304  setErrorString(QLatin1String("QSharedMemory::attach (shmget)"));
305  return false;
306  }
307 
308  // grab the memory
309  memory = shmat(id, 0, (mode == QSharedMemory::ReadOnly ? SHM_RDONLY : 0));
310  if ((void*) - 1 == memory) {
311  memory = 0;
312  setErrorString(QLatin1String("QSharedMemory::attach (shmat)"));
313  return false;
314  }
315 
316  // grab the size
317  shmid_ds shmid_ds;
318  if (!shmctl(id, IPC_STAT, &shmid_ds)) {
319  size = (int)shmid_ds.shm_segsz;
320  } else {
321  setErrorString(QLatin1String("QSharedMemory::attach (shmctl)"));
322  return false;
323  }
324 #else
326 
327  int oflag = (mode == QSharedMemory::ReadOnly ? O_RDONLY : O_RDWR);
328  mode_t omode = (mode == QSharedMemory::ReadOnly ? 0444 : 0660);
329 
330  EINTR_LOOP(hand, shm_open(shmName.constData(), oflag, omode));
331  if (hand == -1) {
332  QString function = QLatin1String("QSharedMemory::attach (shm_open)");
333  switch (errno) {
334  case ENAMETOOLONG:
335  case EINVAL:
336  errorString = QSharedMemory::tr("%1: bad name").arg(function);
338  break;
339  default:
340  setErrorString(function);
341  }
342  hand = 0;
343  return false;
344  }
345 
346  // grab the size
347  QT_STATBUF st;
348  if (QT_FSTAT(hand, &st) == -1) {
349  setErrorString(QLatin1String("QSharedMemory::attach (fstat)"));
350  cleanHandle();
351  return false;
352  }
353  size = st.st_size;
354 
355  // grab the memory
356  int mprot = (mode == QSharedMemory::ReadOnly ? PROT_READ : PROT_READ | PROT_WRITE);
357  memory = mmap(0, size, mprot, MAP_SHARED, hand, 0);
358  if (memory == MAP_FAILED || !memory) {
359  setErrorString(QLatin1String("QSharedMemory::attach (mmap)"));
360  cleanHandle();
361  memory = 0;
362  size = 0;
363  return false;
364  }
365 #endif // QT_POSIX_IPC
366 
367  return true;
368 }
369 
371 {
372 #ifndef QT_POSIX_IPC
373  // detach from the memory segment
374  if (-1 == shmdt(memory)) {
375  QString function = QLatin1String("QSharedMemory::detach");
376  switch (errno) {
377  case EINVAL:
378  errorString = QSharedMemory::tr("%1: not attached").arg(function);
380  break;
381  default:
382  setErrorString(function);
383  }
384  return false;
385  }
386  memory = 0;
387  size = 0;
388 
389  // Get the number of current attachments
390  int id = shmget(unix_key, 0, 0400);
391  cleanHandle();
392 
393  struct shmid_ds shmid_ds;
394  if (0 != shmctl(id, IPC_STAT, &shmid_ds)) {
395  switch (errno) {
396  case EINVAL:
397  return true;
398  default:
399  return false;
400  }
401  }
402  // If there are no attachments then remove it.
403  if (shmid_ds.shm_nattch == 0) {
404  // mark for removal
405  if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) {
406  setErrorString(QLatin1String("QSharedMemory::detach"));
407  switch (errno) {
408  case EINVAL:
409  return true;
410  default:
411  return false;
412  }
413  }
414 
415  // remove file
416  if (!QFile::remove(nativeKey))
417  return false;
418  }
419 #else
420  // detach from the memory segment
421  if (munmap(memory, size) == -1) {
422  setErrorString(QLatin1String("QSharedMemory::detach (munmap)"));
423  return false;
424  }
425  memory = 0;
426  size = 0;
427 
428  // get the number of current attachments
429  int shm_nattch = 0;
430  QT_STATBUF st;
431  if (QT_FSTAT(hand, &st) == 0) {
432  // subtract 2 from linkcount: one for our own open and one for the dir entry
433  shm_nattch = st.st_nlink - 2;
434  }
435  cleanHandle();
436  // if there are no attachments then unlink the shared memory
437  if (shm_nattch == 0) {
439  if (shm_unlink(shmName.constData()) == -1 && errno != ENOENT)
440  setErrorString(QLatin1String("QSharedMemory::detach (shm_unlink)"));
441  }
442 #endif // QT_POSIX_IPC
443 
444  return true;
445 }
446 
448 
449 #endif // QT_NO_SHAREDMEMORY
#define MAP_FAILED
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
QSharedMemory::SharedMemoryError error
#define EINTR_LOOP(var, cmd)
Definition: qcore_unix_p.h:96
#define error(msg)
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:135
void setErrorString(const QString &function)
#define O_RDONLY
static QString tr(const char *sourceText, const char *comment=0, int n=-1)
QLatin1String(DBUS_INTERFACE_DBUS))) Q_GLOBAL_STATIC_WITH_ARGS(QString
static int qt_safe_close(int fd)
Definition: qcore_unix_p.h:297
The QString class provides a Unicode character string.
Definition: qstring.h:83
#define O_CREAT
bool exists() const
Returns true if the file specified by fileName() exists; otherwise returns false. ...
Definition: qfile.cpp:626
Q_CORE_EXPORT void qDebug(const char *,...)
int mode_t
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
static int qt_safe_open(const char *pathname, int flags, mode_t mode=0777)
Definition: qcore_unix_p.h:171
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition: qstring.h:704
HANDLE handle()
If not already made create the handle used for accessing the shared memory.
bool attach(QSharedMemory::AccessMode mode)
static int createUnixKeyFile(const QString &fileName)
Creates the unix file if needed.
const char * constData() const
Returns a pointer to the data stored in the byte array.
Definition: qbytearray.h:433
#define QT_NO_SYSTEMSEMAPHORE
QString arg(qlonglong a, int fieldwidth=0, int base=10, const QChar &fillChar=QLatin1Char(' ')) const Q_REQUIRED_RESULT
Definition: qstring.cpp:7186
#define st(var, type, card)
The QSharedMemory class provides access to a shared memory segment.
Definition: qsharedmemory.h:57
bool remove()
Removes the file specified by fileName().
Definition: qfile.cpp:715
static QByteArray encodeName(const QString &fileName)
By default, this function converts fileName to the local 8-bit encoding determined by the user&#39;s loca...
Definition: qfile.cpp:528
static QString makePlatformSafeKey(const QString &key, const QString &prefix=QLatin1String("qipc_sharedmemory_"))
Generate a string from the key which can be any unicode string into the subset that the win/unix kern...
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
static QString fileName(const QString &fileUrl)
#define O_RDWR
#define O_EXCL
int errno