67ad051 by Lars Knoll at 2009-03-23 1
/****************************************************************************
2
**
89c08c0 by Jason McDonald at 2012-01-11 3
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
04e3b30 by Jason McDonald at 2009-09-09 4
** All rights reserved.
858c70f by Jason McDonald at 2009-06-16 5
** Contact: Nokia Corporation (qt-info@nokia.com)
67ad051 by Lars Knoll at 2009-03-23 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
1eea52e by Jyri Tahtela at 2011-05-13 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.
67ad051 by Lars Knoll at 2009-03-23 17
**
04e3b30 by Jason McDonald at 2009-09-09 18
** In addition, as a special exception, Nokia gives you certain additional
1eea52e by Jyri Tahtela at 2011-05-13 19
** rights. These rights are described in the Nokia Qt LGPL Exception
04e3b30 by Jason McDonald at 2009-09-09 20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
67ad051 by Lars Knoll at 2009-03-23 21
**
1eea52e by Jyri Tahtela at 2011-05-13 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.
309db73 by Jason McDonald at 2009-08-31 29
**
1eea52e by Jyri Tahtela at 2011-05-13 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.
309db73 by Jason McDonald at 2009-08-31 33
**
34
**
35
**
36
**
67ad051 by Lars Knoll at 2009-03-23 37
**
38
** $QT_END_LICENSE$
39
**
40
****************************************************************************/
41
42
#include "qplatformdefs.h"
43
44
#include "qsharedmemory.h"
45
#include "qsharedmemory_p.h"
46
11bacd3 by Ritt Konstantin at 2011-06-06 47
#include <qdebug.h>
48
#include <qfile.h>
e5fcad3 by Lars Knoll at 2009-03-23 49
8d174d6 by Jørgen Lind at 2009-11-16 50
#ifndef QT_NO_SHAREDMEMORY
67ad051 by Lars Knoll at 2009-03-23 51
#include <sys/types.h>
52
#include <sys/ipc.h>
d869e1a by Ritt Konstantin at 2011-06-21 53
#ifndef QT_POSIX_IPC
67ad051 by Lars Knoll at 2009-03-23 54
#include <sys/shm.h>
d869e1a by Ritt Konstantin at 2011-06-21 55
#else
56
#include <sys/mman.h>
67ad051 by Lars Knoll at 2009-03-23 57
#include <sys/stat.h>
d869e1a by Ritt Konstantin at 2011-06-21 58
#endif
67ad051 by Lars Knoll at 2009-03-23 59
#include <fcntl.h>
60
#include <unistd.h>
11bacd3 by Ritt Konstantin at 2011-06-06 61
#endif // QT_NO_SHAREDMEMORY
62
#include <errno.h>
67ad051 by Lars Knoll at 2009-03-23 63
29302d2 by Thiago Macieira at 2009-07-02 64
#include "private/qcore_unix_p.h"
65
3117574 by Jørgen Lind at 2009-10-29 66
#ifndef QT_NO_SHAREDMEMORY
11bacd3 by Ritt Konstantin at 2011-06-06 67
68
//#define QSHAREDMEMORY_DEBUG
69
f63976b by Robert Griebl at 2009-04-07 70
QT_BEGIN_NAMESPACE
71
67ad051 by Lars Knoll at 2009-03-23 72
QSharedMemoryPrivate::QSharedMemoryPrivate()
73
    : QObjectPrivate(), memory(0), size(0), error(QSharedMemory::NoError),
74
#ifndef QT_NO_SYSTEMSEMAPHORE
75
      systemSemaphore(QString()), lockedByMe(false),
76
#endif
d869e1a by Ritt Konstantin at 2011-06-21 77
#ifndef QT_POSIX_IPC
67ad051 by Lars Knoll at 2009-03-23 78
      unix_key(0)
d869e1a by Ritt Konstantin at 2011-06-21 79
#else
80
      hand(0)
81
#endif
67ad051 by Lars Knoll at 2009-03-23 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:
11bacd3 by Ritt Konstantin at 2011-06-06 90
    case EPERM:
67ad051 by Lars Knoll at 2009-03-23 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:
c9e6533 by Volker Hilsheimer at 2009-07-16 99
        errorString = QSharedMemory::tr("%1: doesn't exist").arg(function);
67ad051 by Lars Knoll at 2009-03-23 100
        error = QSharedMemory::NotFound;
101
        break;
d869e1a by Ritt Konstantin at 2011-06-21 102
    case EAGAIN:
67ad051 by Lars Knoll at 2009-03-23 103
    case EMFILE:
d869e1a by Ritt Konstantin at 2011-06-21 104
    case ENFILE:
67ad051 by Lars Knoll at 2009-03-23 105
    case ENOMEM:
106
    case ENOSPC:
107
        errorString = QSharedMemory::tr("%1: out of resources").arg(function);
108
        error = QSharedMemory::OutOfResources;
109
        break;
d869e1a by Ritt Konstantin at 2011-06-21 110
    case EOVERFLOW:
111
        errorString = QSharedMemory::tr("%1: invalid size").arg(function);
112
        error = QSharedMemory::InvalidSize;
113
        break;
67ad051 by Lars Knoll at 2009-03-23 114
    default:
115
        errorString = QSharedMemory::tr("%1: unknown error %2").arg(function).arg(errno);
116
        error = QSharedMemory::UnknownError;
11bacd3 by Ritt Konstantin at 2011-06-06 117
#ifdef QSHAREDMEMORY_DEBUG
67ad051 by Lars Knoll at 2009-03-23 118
        qDebug() << errorString << "key" << key << "errno" << errno << EINVAL;
119
#endif
11bacd3 by Ritt Konstantin at 2011-06-06 120
        break;
67ad051 by Lars Knoll at 2009-03-23 121
    }
122
}
123
124
/*!
125
    \internal
126
127
    If not already made create the handle used for accessing the shared memory.
128
*/
d869e1a by Ritt Konstantin at 2011-06-21 129
#ifndef QT_POSIX_IPC
67ad051 by Lars Knoll at 2009-03-23 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
97a092b by Mirko Damiani at 2010-05-26 137
    if (nativeKey.isEmpty()) {
11bacd3 by Ritt Konstantin at 2011-06-06 138
        errorString = QSharedMemory::tr("%1: key is empty").arg(QLatin1String("QSharedMemory::handle"));
67ad051 by Lars Knoll at 2009-03-23 139
        error = QSharedMemory::KeyError;
140
        return 0;
141
    }
142
143
    // ftok requires that an actual file exists somewhere
97a092b by Mirko Damiani at 2010-05-26 144
    if (!QFile::exists(nativeKey)) {
11bacd3 by Ritt Konstantin at 2011-06-06 145
        errorString = QSharedMemory::tr("%1: UNIX key file doesn't exist").arg(QLatin1String("QSharedMemory::handle"));
67ad051 by Lars Knoll at 2009-03-23 146
        error = QSharedMemory::NotFound;
147
        return 0;
148
    }
149
97a092b by Mirko Damiani at 2010-05-26 150
    unix_key = ftok(QFile::encodeName(nativeKey).constData(), 'Q');
67ad051 by Lars Knoll at 2009-03-23 151
    if (-1 == unix_key) {
11bacd3 by Ritt Konstantin at 2011-06-06 152
        errorString = QSharedMemory::tr("%1: ftok failed").arg(QLatin1String("QSharedMemory::handle"));
67ad051 by Lars Knoll at 2009-03-23 153
        error = QSharedMemory::KeyError;
154
        unix_key = 0;
155
    }
156
    return unix_key;
157
}
d869e1a by Ritt Konstantin at 2011-06-21 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
67ad051 by Lars Knoll at 2009-03-23 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
11bacd3 by Ritt Konstantin at 2011-06-06 184
*/
67ad051 by Lars Knoll at 2009-03-23 185
int QSharedMemoryPrivate::createUnixKeyFile(const QString &fileName)
186
{
d869e1a by Ritt Konstantin at 2011-06-21 187
#ifndef QT_POSIX_IPC
67ad051 by Lars Knoll at 2009-03-23 188
    if (QFile::exists(fileName))
189
        return 0;
190
29302d2 by Thiago Macieira at 2009-07-02 191
    int fd = qt_safe_open(QFile::encodeName(fileName).constData(),
11bacd3 by Ritt Konstantin at 2011-06-06 192
                          O_EXCL | O_CREAT | O_RDWR, 0640);
67ad051 by Lars Knoll at 2009-03-23 193
    if (-1 == fd) {
194
        if (errno == EEXIST)
195
            return 0;
196
        return -1;
197
    } else {
1c2a9b1 by Ritt Konstantin at 2011-06-06 198
        qt_safe_close(fd);
67ad051 by Lars Knoll at 2009-03-23 199
    }
200
    return 1;
d869e1a by Ritt Konstantin at 2011-06-21 201
#else
202
    Q_UNUSED(fileName);
203
    // nothing to do
204
    return -1;
205
#endif
67ad051 by Lars Knoll at 2009-03-23 206
}
207
#endif // QT_NO_SHAREDMEMORY && QT_NO_SYSTEMSEMAPHORE
208
209
#ifndef QT_NO_SHAREDMEMORY
210
8eaf39f by Ritt Konstantin at 2011-06-06 211
void QSharedMemoryPrivate::cleanHandle()
67ad051 by Lars Knoll at 2009-03-23 212
{
d869e1a by Ritt Konstantin at 2011-06-21 213
#ifndef QT_POSIX_IPC
67ad051 by Lars Knoll at 2009-03-23 214
    unix_key = 0;
d869e1a by Ritt Konstantin at 2011-06-21 215
#else
216
    qt_safe_close(hand);
217
    hand = 0;
218
#endif
67ad051 by Lars Knoll at 2009-03-23 219
}
220
221
bool QSharedMemoryPrivate::create(int size)
222
{
d869e1a by Ritt Konstantin at 2011-06-21 223
#ifndef QT_POSIX_IPC
67ad051 by Lars Knoll at 2009-03-23 224
    // build file if needed
97a092b by Mirko Damiani at 2010-05-26 225
    int built = createUnixKeyFile(nativeKey);
67ad051 by Lars Knoll at 2009-03-23 226
    if (built == -1) {
11bacd3 by Ritt Konstantin at 2011-06-06 227
        errorString = QSharedMemory::tr("%1: unable to make key").arg(QLatin1String("QSharedMemory::create"));
67ad051 by Lars Knoll at 2009-03-23 228
        error = QSharedMemory::KeyError;
229
        return false;
230
    }
11bacd3 by Ritt Konstantin at 2011-06-06 231
    bool createdFile = built == 1;
67ad051 by Lars Knoll at 2009-03-23 232
233
    // get handle
234
    if (!handle()) {
235
        if (createdFile)
97a092b by Mirko Damiani at 2010-05-26 236
            QFile::remove(nativeKey);
67ad051 by Lars Knoll at 2009-03-23 237
        return false;
238
    }
239
240
    // create
acd04e6 by Ritt Konstantin at 2011-01-21 241
    if (-1 == shmget(unix_key, size, 0666 | IPC_CREAT | IPC_EXCL)) {
67ad051 by Lars Knoll at 2009-03-23 242
        QString function = QLatin1String("QSharedMemory::create");
243
        switch (errno) {
244
        case EINVAL:
11bacd3 by Ritt Konstantin at 2011-06-06 245
            errorString = QSharedMemory::tr("%1: system-imposed size restrictions").arg(function);
67ad051 by Lars Knoll at 2009-03-23 246
            error = QSharedMemory::InvalidSize;
247
            break;
248
        default:
249
            setErrorString(function);
250
        }
251
        if (createdFile && error != QSharedMemory::AlreadyExists)
97a092b by Mirko Damiani at 2010-05-26 252
            QFile::remove(nativeKey);
67ad051 by Lars Knoll at 2009-03-23 253
        return false;
254
    }
d869e1a by Ritt Konstantin at 2011-06-21 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
67ad051 by Lars Knoll at 2009-03-23 288
289
    return true;
290
}
291
292
bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode)
293
{
d869e1a by Ritt Konstantin at 2011-06-21 294
#ifndef QT_POSIX_IPC
67ad051 by Lars Knoll at 2009-03-23 295
    // grab the shared memory segment id
acd04e6 by Ritt Konstantin at 2011-01-21 296
    int id = shmget(unix_key, 0, (mode == QSharedMemory::ReadOnly ? 0444 : 0660));
67ad051 by Lars Knoll at 2009-03-23 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
    }
d869e1a by Ritt Konstantin at 2011-06-21 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
67ad051 by Lars Knoll at 2009-03-23 360
361
    return true;
362
}
363
364
bool QSharedMemoryPrivate::detach()
365
{
d869e1a by Ritt Konstantin at 2011-06-21 366
#ifndef QT_POSIX_IPC
67ad051 by Lars Knoll at 2009-03-23 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;
c17e49e by Ritt Konstantin at 2011-01-21 381
    size = 0;
67ad051 by Lars Knoll at 2009-03-23 382
383
    // Get the number of current attachments
acd04e6 by Ritt Konstantin at 2011-01-21 384
    int id = shmget(unix_key, 0, 0444);
385
    cleanHandle();
67ad051 by Lars Knoll at 2009-03-23 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)) {
11bacd3 by Ritt Konstantin at 2011-06-06 400
            setErrorString(QLatin1String("QSharedMemory::detach"));
67ad051 by Lars Knoll at 2009-03-23 401
            switch (errno) {
402
            case EINVAL:
403
                return true;
404
            default:
405
                return false;
406
            }
407
        }
408
409
        // remove file
97a092b by Mirko Damiani at 2010-05-26 410
        if (!QFile::remove(nativeKey))
67ad051 by Lars Knoll at 2009-03-23 411
            return false;
412
    }
d869e1a by Ritt Konstantin at 2011-06-21 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
67ad051 by Lars Knoll at 2009-03-23 438
    return true;
439
}
440
441
QT_END_NAMESPACE
442
443
#endif // QT_NO_SHAREDMEMORY