1
/****************************************************************************
2
**
3
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
7
** This file is part of the QtCore module of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** GNU Lesser General Public License Usage
11
** This file may be used under the terms of the GNU Lesser General Public
12
** License version 2.1 as published by the Free Software Foundation and
13
** appearing in the file LICENSE.LGPL included in the packaging of this
14
** file. Please review the following information to ensure the GNU Lesser
15
** General Public License version 2.1 requirements will be met:
16
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17
**
18
** In addition, as a special exception, Nokia gives you certain additional
19
** rights. These rights are described in the Nokia Qt LGPL Exception
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21
**
22
** GNU General Public License Usage
23
** Alternatively, this file may be used under the terms of the GNU General
24
** Public License version 3.0 as published by the Free Software Foundation
25
** and appearing in the file LICENSE.GPL included in the packaging of this
26
** file. Please review the following information to ensure the GNU General
27
** Public License version 3.0 requirements will be met:
28
** http://www.gnu.org/copyleft/gpl.html.
29
**
30
** Other Usage
31
** Alternatively, this file may be used in accordance with the terms and
32
** conditions contained in a signed written agreement between you and Nokia.
33
**
34
**
35
**
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
70
QT_BEGIN_NAMESPACE
71
72
QSharedMemoryPrivate::QSharedMemoryPrivate()
73
    : QObjectPrivate(), memory(0), size(0), error(QSharedMemory::NoError),
74
#ifndef QT_NO_SYSTEMSEMAPHORE
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
85
void QSharedMemoryPrivate::setErrorString(const QString &function)
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);
92
        error = QSharedMemory::PermissionDenied;
93
        break;
94
    case EEXIST:
95
        errorString = QSharedMemory::tr("%1: already exists").arg(function);
96
        error = QSharedMemory::AlreadyExists;
97
        break;
98
    case ENOENT:
99
        errorString = QSharedMemory::tr("%1: doesn't exist").arg(function);
100
        error = QSharedMemory::NotFound;
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);
108
        error = QSharedMemory::OutOfResources;
109
        break;
110
    case EOVERFLOW:
111
        errorString = QSharedMemory::tr("%1: invalid size").arg(function);
112
        error = QSharedMemory::InvalidSize;
113
        break;
114
    default:
115
        errorString = QSharedMemory::tr("%1: unknown error %2").arg(function).arg(errno);
116
        error = QSharedMemory::UnknownError;
117
#ifdef QSHAREDMEMORY_DEBUG
118
        qDebug() << errorString << "key" << key << "errno" << errno << EINVAL;
119
#endif
120
        break;
121
    }
122
}
123
124
/*!
125
    \internal
126
127
    If not already made create the handle used for accessing the shared memory.
128
*/
129
#ifndef QT_POSIX_IPC
130
key_t QSharedMemoryPrivate::handle()
131
{
132
    // already made
133
    if (unix_key)
134
        return unix_key;
135
136
    // don't allow making handles on empty keys
137
    if (nativeKey.isEmpty()) {
138
        errorString = QSharedMemory::tr("%1: key is empty").arg(QLatin1String("QSharedMemory::handle"));
139
        error = QSharedMemory::KeyError;
140
        return 0;
141
    }
142
143
    // ftok requires that an actual file exists somewhere
144
    if (!QFile::exists(nativeKey)) {
145
        errorString = QSharedMemory::tr("%1: UNIX key file doesn't exist").arg(QLatin1String("QSharedMemory::handle"));
146
        error = QSharedMemory::NotFound;
147
        return 0;
148
    }
149
150
    unix_key = ftok(QFile::encodeName(nativeKey).constData(), 'Q');
151
    if (-1 == unix_key) {
152
        errorString = QSharedMemory::tr("%1: ftok failed").arg(QLatin1String("QSharedMemory::handle"));
153
        error = QSharedMemory::KeyError;
154
        unix_key = 0;
155
    }
156
    return unix_key;
157
}
158
#else
159
int QSharedMemoryPrivate::handle()
160
{
161
    // don't allow making handles on empty keys
162
    QString safeKey = makePlatformSafeKey(key);
163
    if (safeKey.isEmpty()) {
164
        errorString = QSharedMemory::tr("%1: key is empty").arg(QLatin1String("QSharedMemory::handle"));
165
        error = QSharedMemory::KeyError;
166
        return 0;
167
    }
168
169
    return 1;
170
}
171
#endif // QT_POSIX_IPC
172
173
#endif // QT_NO_SHAREDMEMORY
174
175
#if !(defined(QT_NO_SHAREDMEMORY) && defined(QT_NO_SYSTEMSEMAPHORE))
176
/*!
177
    \internal
178
    Creates the unix file if needed.
179
    returns true if the unix file was created.
180
181
    -1 error
182
     0 already existed
183
     1 created
184
*/
185
int QSharedMemoryPrivate::createUnixKeyFile(const QString &fileName)
186
{
187
#ifndef QT_POSIX_IPC
188
    if (QFile::exists(fileName))
189
        return 0;
190
191
    int fd = qt_safe_open(QFile::encodeName(fileName).constData(),
192
                          O_EXCL | O_CREAT | O_RDWR, 0640);
193
    if (-1 == fd) {
194
        if (errno == EEXIST)
195
            return 0;
196
        return -1;
197
    } else {
198
        qt_safe_close(fd);
199
    }
200
    return 1;
201
#else
202
    Q_UNUSED(fileName);
203
    // nothing to do
204
    return -1;
205
#endif
206
}
207
#endif // QT_NO_SHAREDMEMORY && QT_NO_SYSTEMSEMAPHORE
208
209
#ifndef QT_NO_SHAREDMEMORY
210
211
void QSharedMemoryPrivate::cleanHandle()
212
{
213
#ifndef QT_POSIX_IPC
214
    unix_key = 0;
215
#else
216
    qt_safe_close(hand);
217
    hand = 0;
218
#endif
219
}
220
221
bool QSharedMemoryPrivate::create(int size)
222
{
223
#ifndef QT_POSIX_IPC
224
    // build file if needed
225
    int built = createUnixKeyFile(nativeKey);
226
    if (built == -1) {
227
        errorString = QSharedMemory::tr("%1: unable to make key").arg(QLatin1String("QSharedMemory::create"));
228
        error = QSharedMemory::KeyError;
229
        return false;
230
    }
231
    bool createdFile = built == 1;
232
233
    // get handle
234
    if (!handle()) {
235
        if (createdFile)
236
            QFile::remove(nativeKey);
237
        return false;
238
    }
239
240
    // create
241
    if (-1 == shmget(unix_key, size, 0666 | IPC_CREAT | IPC_EXCL)) {
242
        QString function = QLatin1String("QSharedMemory::create");
243
        switch (errno) {
244
        case EINVAL:
245
            errorString = QSharedMemory::tr("%1: system-imposed size restrictions").arg(function);
246
            error = QSharedMemory::InvalidSize;
247
            break;
248
        default:
249
            setErrorString(function);
250
        }
251
        if (createdFile && error != QSharedMemory::AlreadyExists)
252
            QFile::remove(nativeKey);
253
        return false;
254
    }
255
#else
256
    if (!handle())
257
        return false;
258
259
    QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key));
260
261
    int fd;
262
    EINTR_LOOP(fd, shm_open(shmName.constData(), O_RDWR | O_CREAT | O_EXCL, 0666));
263
    if (fd == -1) {
264
        QString function = QLatin1String("QSharedMemory::create");
265
        switch (errno) {
266
        case ENAMETOOLONG:
267
        case EINVAL:
268
            errorString = QSharedMemory::tr("%1: bad name").arg(function);
269
            error = QSharedMemory::KeyError;
270
            break;
271
        default:
272
            setErrorString(function);
273
        }
274
        return false;
275
    }
276
277
    // the size may only be set once; ignore errors
278
    int ret;
279
    EINTR_LOOP(ret, ftruncate(fd, size));
280
    if (ret == -1) {
281
        setErrorString(QLatin1String("QSharedMemory::create (ftruncate)"));
282
        qt_safe_close(fd);
283
        return false;
284
    }
285
286
    qt_safe_close(fd);
287
#endif // QT_POSIX_IPC
288
289
    return true;
290
}
291
292
bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode)
293
{
294
#ifndef QT_POSIX_IPC
295
    // grab the shared memory segment id
296
    int id = shmget(unix_key, 0, (mode == QSharedMemory::ReadOnly ? 0444 : 0660));
297
    if (-1 == id) {
298
        setErrorString(QLatin1String("QSharedMemory::attach (shmget)"));
299
        return false;
300
    }
301
302
    // grab the memory
303
    memory = shmat(id, 0, (mode == QSharedMemory::ReadOnly ? SHM_RDONLY : 0));
304
    if ((void*) - 1 == memory) {
305
        memory = 0;
306
        setErrorString(QLatin1String("QSharedMemory::attach (shmat)"));
307
        return false;
308
    }
309
310
    // grab the size
311
    shmid_ds shmid_ds;
312
    if (!shmctl(id, IPC_STAT, &shmid_ds)) {
313
        size = (int)shmid_ds.shm_segsz;
314
    } else {
315
        setErrorString(QLatin1String("QSharedMemory::attach (shmctl)"));
316
        return false;
317
    }
318
#else
319
    QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key));
320
321
    int oflag = (mode == QSharedMemory::ReadOnly ? O_RDONLY : O_RDWR);
322
    mode_t omode = (mode == QSharedMemory::ReadOnly ? 0444 : 0660);
323
324
    EINTR_LOOP(hand, shm_open(shmName.constData(), oflag, omode));
325
    if (hand == -1) {
326
        QString function = QLatin1String("QSharedMemory::attach (shm_open)");
327
        switch (errno) {
328
        case ENAMETOOLONG:
329
        case EINVAL:
330
            errorString = QSharedMemory::tr("%1: bad name").arg(function);
331
            error = QSharedMemory::KeyError;
332
            break;
333
        default:
334
            setErrorString(function);
335
        }
336
        hand = 0;
337
        return false;
338
    }
339
340
    // grab the size
341
    QT_STATBUF st;
342
    if (QT_FSTAT(hand, &st) == -1) {
343
        setErrorString(QLatin1String("QSharedMemory::attach (fstat)"));
344
        cleanHandle();
345
        return false;
346
    }
347
    size = st.st_size;
348
349
    // grab the memory
350
    int mprot = (mode == QSharedMemory::ReadOnly ? PROT_READ : PROT_READ | PROT_WRITE);
351
    memory = mmap(0, size, mprot, MAP_SHARED, hand, 0);
352
    if (memory == MAP_FAILED || !memory) {
353
        setErrorString(QLatin1String("QSharedMemory::attach (mmap)"));
354
        cleanHandle();
355
        memory = 0;
356
        size = 0;
357
        return false;
358
    }
359
#endif // QT_POSIX_IPC
360
361
    return true;
362
}
363
364
bool QSharedMemoryPrivate::detach()
365
{
366
#ifndef QT_POSIX_IPC
367
    // detach from the memory segment
368
    if (-1 == shmdt(memory)) {
369
        QString function = QLatin1String("QSharedMemory::detach");
370
        switch (errno) {
371
        case EINVAL:
372
            errorString = QSharedMemory::tr("%1: not attached").arg(function);
373
            error = QSharedMemory::NotFound;
374
            break;
375
        default:
376
            setErrorString(function);
377
        }
378
        return false;
379
    }
380
    memory = 0;
381
    size = 0;
382
383
    // Get the number of current attachments
384
    int id = shmget(unix_key, 0, 0444);
385
    cleanHandle();
386
387
    struct shmid_ds shmid_ds;
388
    if (0 != shmctl(id, IPC_STAT, &shmid_ds)) {
389
        switch (errno) {
390
        case EINVAL:
391
            return true;
392
        default:
393
            return false;
394
        }
395
    }
396
    // If there are no attachments then remove it.
397
    if (shmid_ds.shm_nattch == 0) {
398
        // mark for removal
399
        if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) {
400
            setErrorString(QLatin1String("QSharedMemory::detach"));
401
            switch (errno) {
402
            case EINVAL:
403
                return true;
404
            default:
405
                return false;
406
            }
407
        }
408
409
        // remove file
410
        if (!QFile::remove(nativeKey))
411
            return false;
412
    }
413
#else
414
    // detach from the memory segment
415
    if (munmap(memory, size) == -1) {
416
        setErrorString(QLatin1String("QSharedMemory::detach (munmap)"));
417
        return false;
418
    }
419
    memory = 0;
420
    size = 0;
421
422
    // get the number of current attachments
423
    int shm_nattch = 0;
424
    QT_STATBUF st;
425
    if (QT_FSTAT(hand, &st) == 0) {
426
        // subtract 2 from linkcount: one for our own open and one for the dir entry
427
        shm_nattch = st.st_nlink - 2;
428
    }
429
    cleanHandle();
430
    // if there are no attachments then unlink the shared memory
431
    if (shm_nattch == 0) {
432
        QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key));
433
        if (shm_unlink(shmName.constData()) == -1 && errno != ENOENT)
434
            setErrorString(QLatin1String("QSharedMemory::detach (shm_unlink)"));
435
    }
436
#endif // QT_POSIX_IPC
437
438
    return true;
439
}
440
441
QT_END_NAMESPACE
442
443
#endif // QT_NO_SHAREDMEMORY