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 "qsharedmemory.h" |
41 | #include "qsharedmemory_p.h" |
42 | #include "qsystemsemaphore.h" |
43 | #include <qdir.h> |
44 | #include <qcryptographichash.h> |
45 | #include <qdebug.h> |
46 | #ifdef Q_OS_WIN |
47 | # include <qt_windows.h> |
48 | #endif |
49 | |
50 | QT_BEGIN_NAMESPACE |
51 | |
52 | #if !(defined(QT_NO_SHAREDMEMORY) && defined(QT_NO_SYSTEMSEMAPHORE)) |
53 | /*! |
54 | \internal |
55 | |
56 | Generate a string from the key which can be any unicode string into |
57 | the subset that the win/unix kernel allows. |
58 | |
59 | On Unix this will be a file name |
60 | */ |
61 | QString |
62 | QSharedMemoryPrivate::makePlatformSafeKey(const QString &key, |
63 | const QString &prefix) |
64 | { |
65 | if (key.isEmpty()) |
66 | return QString(); |
67 | |
68 | QString result = prefix; |
69 | |
70 | for (QChar ch : key) { |
71 | if ((ch >= QLatin1Char('a') && ch <= QLatin1Char('z')) || |
72 | (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z'))) |
73 | result += ch; |
74 | } |
75 | |
76 | QByteArray hex = QCryptographicHash::hash(data: key.toUtf8(), method: QCryptographicHash::Sha1).toHex(); |
77 | result.append(s: QLatin1String(hex)); |
78 | #ifdef Q_OS_WIN |
79 | return result; |
80 | #elif defined(QT_POSIX_IPC) |
81 | return QLatin1Char('/') + result; |
82 | #else |
83 | return QDir::tempPath() + QLatin1Char('/') + result; |
84 | #endif |
85 | } |
86 | #endif // QT_NO_SHAREDMEMORY && QT_NO_SHAREDMEMORY |
87 | |
88 | #ifndef QT_NO_SHAREDMEMORY |
89 | |
90 | /*! |
91 | \class QSharedMemory |
92 | \inmodule QtCore |
93 | \since 4.4 |
94 | |
95 | \brief The QSharedMemory class provides access to a shared memory segment. |
96 | |
97 | QSharedMemory provides access to a shared memory segment by multiple |
98 | threads and processes. It also provides a way for a single thread or |
99 | process to lock the memory for exclusive access. |
100 | |
101 | When using this class, be aware of the following platform |
102 | differences: |
103 | |
104 | \list |
105 | |
106 | \li Windows: QSharedMemory does not "own" the shared memory segment. |
107 | When all threads or processes that have an instance of QSharedMemory |
108 | attached to a particular shared memory segment have either destroyed |
109 | their instance of QSharedMemory or exited, the Windows kernel |
110 | releases the shared memory segment automatically. |
111 | |
112 | \li Unix: QSharedMemory "owns" the shared memory segment. When the |
113 | last thread or process that has an instance of QSharedMemory |
114 | attached to a particular shared memory segment detaches from the |
115 | segment by destroying its instance of QSharedMemory, the Unix kernel |
116 | release the shared memory segment. But if that last thread or |
117 | process crashes without running the QSharedMemory destructor, the |
118 | shared memory segment survives the crash. |
119 | |
120 | \li HP-UX: Only one attach to a shared memory segment is allowed per |
121 | process. This means that QSharedMemory should not be used across |
122 | multiple threads in the same process in HP-UX. |
123 | |
124 | \endlist |
125 | |
126 | Remember to lock the shared memory with lock() before reading from |
127 | or writing to the shared memory, and remember to release the lock |
128 | with unlock() after you are done. |
129 | |
130 | QSharedMemory automatically destroys the shared memory segment when |
131 | the last instance of QSharedMemory is detached from the segment, and |
132 | no references to the segment remain. |
133 | |
134 | \warning QSharedMemory changes the key in a Qt-specific way, unless otherwise |
135 | specified. Interoperation with non-Qt applications is achieved by first creating |
136 | a default shared memory with QSharedMemory() and then setting a native key with |
137 | setNativeKey(). When using native keys, shared memory is not protected against |
138 | multiple accesses on it (for example, unable to lock()) and a user-defined mechanism |
139 | should be used to achieve such protection. |
140 | */ |
141 | |
142 | /*! |
143 | \overload QSharedMemory() |
144 | |
145 | Constructs a shared memory object with the given \a parent. The |
146 | shared memory object's key is not set by the constructor, so the |
147 | shared memory object does not have an underlying shared memory |
148 | segment attached. The key must be set with setKey() or setNativeKey() |
149 | before create() or attach() can be used. |
150 | |
151 | \sa setKey() |
152 | */ |
153 | |
154 | #ifndef QT_NO_QOBJECT |
155 | QSharedMemory::QSharedMemory(QObject *parent) |
156 | : QObject(*new QSharedMemoryPrivate, parent) |
157 | { |
158 | } |
159 | #else |
160 | QSharedMemory::QSharedMemory() |
161 | : d_ptr(new QSharedMemoryPrivate) |
162 | { |
163 | } |
164 | #endif |
165 | /*! |
166 | Constructs a shared memory object with the given \a parent and with |
167 | its key set to \a key. Because its key is set, its create() and |
168 | attach() functions can be called. |
169 | |
170 | \sa setKey(), create(), attach() |
171 | */ |
172 | #ifndef QT_NO_QOBJECT |
173 | QSharedMemory::QSharedMemory(const QString &key, QObject *parent) |
174 | : QObject(*new QSharedMemoryPrivate, parent) |
175 | { |
176 | setKey(key); |
177 | } |
178 | #else |
179 | QSharedMemory::QSharedMemory(const QString &key) |
180 | : d_ptr(new QSharedMemoryPrivate) |
181 | { |
182 | setKey(key); |
183 | } |
184 | #endif |
185 | |
186 | /*! |
187 | The destructor clears the key, which forces the shared memory object |
188 | to \l {detach()} {detach} from its underlying shared memory |
189 | segment. If this shared memory object is the last one connected to |
190 | the shared memory segment, the detach() operation destroys the |
191 | shared memory segment. |
192 | |
193 | \sa detach(), isAttached() |
194 | */ |
195 | QSharedMemory::~QSharedMemory() |
196 | { |
197 | setKey(QString()); |
198 | } |
199 | |
200 | /*! |
201 | Sets the platform independent \a key for this shared memory object. If \a key |
202 | is the same as the current key, the function returns without doing anything. |
203 | |
204 | You can call key() to retrieve the platform independent key. Internally, |
205 | QSharedMemory converts this key into a platform specific key. If you instead |
206 | call nativeKey(), you will get the platform specific, converted key. |
207 | |
208 | If the shared memory object is attached to an underlying shared memory |
209 | segment, it will \l {detach()} {detach} from it before setting the new key. |
210 | This function does not do an attach(). |
211 | |
212 | \sa key(), nativeKey(), isAttached() |
213 | */ |
214 | void QSharedMemory::setKey(const QString &key) |
215 | { |
216 | Q_D(QSharedMemory); |
217 | if (key == d->key && d->makePlatformSafeKey(key) == d->nativeKey) |
218 | return; |
219 | |
220 | if (isAttached()) |
221 | detach(); |
222 | d->cleanHandle(); |
223 | d->key = key; |
224 | d->nativeKey = d->makePlatformSafeKey(key); |
225 | } |
226 | |
227 | /*! |
228 | \since 4.8 |
229 | |
230 | Sets the native, platform specific, \a key for this shared memory object. If |
231 | \a key is the same as the current native key, the function returns without |
232 | doing anything. If all you want is to assign a key to a segment, you should |
233 | call setKey() instead. |
234 | |
235 | You can call nativeKey() to retrieve the native key. If a native key has been |
236 | assigned, calling key() will return a null string. |
237 | |
238 | If the shared memory object is attached to an underlying shared memory |
239 | segment, it will \l {detach()} {detach} from it before setting the new key. |
240 | This function does not do an attach(). |
241 | |
242 | The application will not be portable if you set a native key. |
243 | |
244 | \sa nativeKey(), key(), isAttached() |
245 | */ |
246 | void QSharedMemory::setNativeKey(const QString &key) |
247 | { |
248 | Q_D(QSharedMemory); |
249 | if (key == d->nativeKey && d->key.isNull()) |
250 | return; |
251 | |
252 | if (isAttached()) |
253 | detach(); |
254 | d->cleanHandle(); |
255 | d->key = QString(); |
256 | d->nativeKey = key; |
257 | } |
258 | |
259 | bool QSharedMemoryPrivate::initKey() |
260 | { |
261 | if (!cleanHandle()) |
262 | return false; |
263 | #ifndef QT_NO_SYSTEMSEMAPHORE |
264 | systemSemaphore.setKey(key: QString(), initialValue: 1); |
265 | systemSemaphore.setKey(key, initialValue: 1); |
266 | if (systemSemaphore.error() != QSystemSemaphore::NoError) { |
267 | QString function = QLatin1String("QSharedMemoryPrivate::initKey" ); |
268 | errorString = QSharedMemory::tr(s: "%1: unable to set key on lock" ).arg(a: function); |
269 | switch(systemSemaphore.error()) { |
270 | case QSystemSemaphore::PermissionDenied: |
271 | error = QSharedMemory::PermissionDenied; |
272 | break; |
273 | case QSystemSemaphore::KeyError: |
274 | error = QSharedMemory::KeyError; |
275 | break; |
276 | case QSystemSemaphore::AlreadyExists: |
277 | error = QSharedMemory::AlreadyExists; |
278 | break; |
279 | case QSystemSemaphore::NotFound: |
280 | error = QSharedMemory::NotFound; |
281 | break; |
282 | case QSystemSemaphore::OutOfResources: |
283 | error = QSharedMemory::OutOfResources; |
284 | break; |
285 | case QSystemSemaphore::UnknownError: |
286 | default: |
287 | error = QSharedMemory::UnknownError; |
288 | break; |
289 | } |
290 | return false; |
291 | } |
292 | #endif |
293 | errorString = QString(); |
294 | error = QSharedMemory::NoError; |
295 | return true; |
296 | } |
297 | |
298 | /*! |
299 | Returns the key assigned with setKey() to this shared memory, or a null key |
300 | if no key has been assigned, or if the segment is using a nativeKey(). The |
301 | key is the identifier used by Qt applications to identify the shared memory |
302 | segment. |
303 | |
304 | You can find the native, platform specific, key used by the operating system |
305 | by calling nativeKey(). |
306 | |
307 | \sa setKey(), setNativeKey() |
308 | */ |
309 | QString QSharedMemory::key() const |
310 | { |
311 | Q_D(const QSharedMemory); |
312 | return d->key; |
313 | } |
314 | |
315 | /*! |
316 | \since 4.8 |
317 | |
318 | Returns the native, platform specific, key for this shared memory object. The |
319 | native key is the identifier used by the operating system to identify the |
320 | shared memory segment. |
321 | |
322 | You can use the native key to access shared memory segments that have not |
323 | been created by Qt, or to grant shared memory access to non-Qt applications. |
324 | |
325 | \sa setKey(), setNativeKey() |
326 | */ |
327 | QString QSharedMemory::nativeKey() const |
328 | { |
329 | Q_D(const QSharedMemory); |
330 | return d->nativeKey; |
331 | } |
332 | |
333 | /*! |
334 | Creates a shared memory segment of \a size bytes with the key passed to the |
335 | constructor, set with setKey() or set with setNativeKey(), then attaches to |
336 | the new shared memory segment with the given access \a mode and returns |
337 | \tt true. If a shared memory segment identified by the key already exists, |
338 | the attach operation is not performed and \tt false is returned. When the |
339 | return value is \tt false, call error() to determine which error occurred. |
340 | |
341 | \sa error() |
342 | */ |
343 | bool QSharedMemory::create(int size, AccessMode mode) |
344 | { |
345 | Q_D(QSharedMemory); |
346 | |
347 | if (!d->initKey()) |
348 | return false; |
349 | |
350 | #ifndef QT_NO_SYSTEMSEMAPHORE |
351 | #ifndef Q_OS_WIN |
352 | // Take ownership and force set initialValue because the semaphore |
353 | // might have already existed from a previous crash. |
354 | d->systemSemaphore.setKey(key: d->key, initialValue: 1, mode: QSystemSemaphore::Create); |
355 | #endif |
356 | #endif |
357 | |
358 | QString function = QLatin1String("QSharedMemory::create" ); |
359 | #ifndef QT_NO_SYSTEMSEMAPHORE |
360 | QSharedMemoryLocker lock(this); |
361 | if (!d->key.isNull() && !d->tryLocker(locker: &lock, function)) |
362 | return false; |
363 | #endif |
364 | |
365 | if (size <= 0) { |
366 | d->error = QSharedMemory::InvalidSize; |
367 | d->errorString = |
368 | QSharedMemory::tr(s: "%1: create size is less then 0" ).arg(a: function); |
369 | return false; |
370 | } |
371 | |
372 | if (!d->create(size)) |
373 | return false; |
374 | |
375 | return d->attach(mode); |
376 | } |
377 | |
378 | /*! |
379 | Returns the size of the attached shared memory segment. If no shared |
380 | memory segment is attached, 0 is returned. |
381 | |
382 | \note The size of the segment may be larger than the requested size that was |
383 | passed to create(). |
384 | |
385 | \sa create(), attach() |
386 | */ |
387 | int QSharedMemory::size() const |
388 | { |
389 | Q_D(const QSharedMemory); |
390 | return d->size; |
391 | } |
392 | |
393 | /*! |
394 | \enum QSharedMemory::AccessMode |
395 | |
396 | \value ReadOnly The shared memory segment is read-only. Writing to |
397 | the shared memory segment is not allowed. An attempt to write to a |
398 | shared memory segment created with ReadOnly causes the program to |
399 | abort. |
400 | |
401 | \value ReadWrite Reading and writing the shared memory segment are |
402 | both allowed. |
403 | */ |
404 | |
405 | /*! |
406 | Attempts to attach the process to the shared memory segment |
407 | identified by the key that was passed to the constructor or to a |
408 | call to setKey() or setNativeKey(). The access \a mode is \l {QSharedMemory::} |
409 | {ReadWrite} by default. It can also be \l {QSharedMemory::} |
410 | {ReadOnly}. Returns \c true if the attach operation is successful. If |
411 | false is returned, call error() to determine which error occurred. |
412 | After attaching the shared memory segment, a pointer to the shared |
413 | memory can be obtained by calling data(). |
414 | |
415 | \sa isAttached(), detach(), create() |
416 | */ |
417 | bool QSharedMemory::attach(AccessMode mode) |
418 | { |
419 | Q_D(QSharedMemory); |
420 | |
421 | if (isAttached() || !d->initKey()) |
422 | return false; |
423 | #ifndef QT_NO_SYSTEMSEMAPHORE |
424 | QSharedMemoryLocker lock(this); |
425 | if (!d->key.isNull() && !d->tryLocker(locker: &lock, function: QLatin1String("QSharedMemory::attach" ))) |
426 | return false; |
427 | #endif |
428 | |
429 | if (isAttached() || !d->handle()) |
430 | return false; |
431 | |
432 | return d->attach(mode); |
433 | } |
434 | |
435 | /*! |
436 | Returns \c true if this process is attached to the shared memory |
437 | segment. |
438 | |
439 | \sa attach(), detach() |
440 | */ |
441 | bool QSharedMemory::isAttached() const |
442 | { |
443 | Q_D(const QSharedMemory); |
444 | return (nullptr != d->memory); |
445 | } |
446 | |
447 | /*! |
448 | Detaches the process from the shared memory segment. If this was the |
449 | last process attached to the shared memory segment, then the shared |
450 | memory segment is released by the system, i.e., the contents are |
451 | destroyed. The function returns \c true if it detaches the shared |
452 | memory segment. If it returns \c false, it usually means the segment |
453 | either isn't attached, or it is locked by another process. |
454 | |
455 | \sa attach(), isAttached() |
456 | */ |
457 | bool QSharedMemory::detach() |
458 | { |
459 | Q_D(QSharedMemory); |
460 | if (!isAttached()) |
461 | return false; |
462 | |
463 | #ifndef QT_NO_SYSTEMSEMAPHORE |
464 | QSharedMemoryLocker lock(this); |
465 | if (!d->key.isNull() && !d->tryLocker(locker: &lock, function: QLatin1String("QSharedMemory::detach" ))) |
466 | return false; |
467 | #endif |
468 | |
469 | return d->detach(); |
470 | } |
471 | |
472 | /*! |
473 | Returns a pointer to the contents of the shared memory segment, if |
474 | one is attached. Otherwise it returns null. Remember to lock the |
475 | shared memory with lock() before reading from or writing to the |
476 | shared memory, and remember to release the lock with unlock() after |
477 | you are done. |
478 | |
479 | \sa attach() |
480 | */ |
481 | void *QSharedMemory::data() |
482 | { |
483 | Q_D(QSharedMemory); |
484 | return d->memory; |
485 | } |
486 | |
487 | /*! |
488 | Returns a const pointer to the contents of the shared memory |
489 | segment, if one is attached. Otherwise it returns null. Remember to |
490 | lock the shared memory with lock() before reading from or writing to |
491 | the shared memory, and remember to release the lock with unlock() |
492 | after you are done. |
493 | |
494 | \sa attach(), create() |
495 | */ |
496 | const void* QSharedMemory::constData() const |
497 | { |
498 | Q_D(const QSharedMemory); |
499 | return d->memory; |
500 | } |
501 | |
502 | /*! |
503 | \overload data() |
504 | */ |
505 | const void *QSharedMemory::data() const |
506 | { |
507 | Q_D(const QSharedMemory); |
508 | return d->memory; |
509 | } |
510 | |
511 | #ifndef QT_NO_SYSTEMSEMAPHORE |
512 | /*! |
513 | This is a semaphore that locks the shared memory segment for access |
514 | by this process and returns \c true. If another process has locked the |
515 | segment, this function blocks until the lock is released. Then it |
516 | acquires the lock and returns \c true. If this function returns \c false, |
517 | it means that you have ignored a false return from create() or attach(), |
518 | that you have set the key with setNativeKey() or that |
519 | QSystemSemaphore::acquire() failed due to an unknown system error. |
520 | |
521 | \sa unlock(), data(), QSystemSemaphore::acquire() |
522 | */ |
523 | bool QSharedMemory::lock() |
524 | { |
525 | Q_D(QSharedMemory); |
526 | if (d->lockedByMe) { |
527 | qWarning(msg: "QSharedMemory::lock: already locked" ); |
528 | return true; |
529 | } |
530 | if (d->systemSemaphore.acquire()) { |
531 | d->lockedByMe = true; |
532 | return true; |
533 | } |
534 | QString function = QLatin1String("QSharedMemory::lock" ); |
535 | d->errorString = QSharedMemory::tr(s: "%1: unable to lock" ).arg(a: function); |
536 | d->error = QSharedMemory::LockError; |
537 | return false; |
538 | } |
539 | |
540 | /*! |
541 | Releases the lock on the shared memory segment and returns \c true, if |
542 | the lock is currently held by this process. If the segment is not |
543 | locked, or if the lock is held by another process, nothing happens |
544 | and false is returned. |
545 | |
546 | \sa lock() |
547 | */ |
548 | bool QSharedMemory::unlock() |
549 | { |
550 | Q_D(QSharedMemory); |
551 | if (!d->lockedByMe) |
552 | return false; |
553 | d->lockedByMe = false; |
554 | if (d->systemSemaphore.release()) |
555 | return true; |
556 | QString function = QLatin1String("QSharedMemory::unlock" ); |
557 | d->errorString = QSharedMemory::tr(s: "%1: unable to unlock" ).arg(a: function); |
558 | d->error = QSharedMemory::LockError; |
559 | return false; |
560 | } |
561 | #endif // QT_NO_SYSTEMSEMAPHORE |
562 | |
563 | /*! |
564 | \enum QSharedMemory::SharedMemoryError |
565 | |
566 | \value NoError No error occurred. |
567 | |
568 | \value PermissionDenied The operation failed because the caller |
569 | didn't have the required permissions. |
570 | |
571 | \value InvalidSize A create operation failed because the requested |
572 | size was invalid. |
573 | |
574 | \value KeyError The operation failed because of an invalid key. |
575 | |
576 | \value AlreadyExists A create() operation failed because a shared |
577 | memory segment with the specified key already existed. |
578 | |
579 | \value NotFound An attach() failed because a shared memory segment |
580 | with the specified key could not be found. |
581 | |
582 | \value LockError The attempt to lock() the shared memory segment |
583 | failed because create() or attach() failed and returned false, or |
584 | because a system error occurred in QSystemSemaphore::acquire(). |
585 | |
586 | \value OutOfResources A create() operation failed because there was |
587 | not enough memory available to fill the request. |
588 | |
589 | \value UnknownError Something else happened and it was bad. |
590 | */ |
591 | |
592 | /*! |
593 | Returns a value indicating whether an error occurred, and, if so, |
594 | which error it was. |
595 | |
596 | \sa errorString() |
597 | */ |
598 | QSharedMemory::SharedMemoryError QSharedMemory::error() const |
599 | { |
600 | Q_D(const QSharedMemory); |
601 | return d->error; |
602 | } |
603 | |
604 | /*! |
605 | Returns a text description of the last error that occurred. If |
606 | error() returns an \l {QSharedMemory::SharedMemoryError} {error |
607 | value}, call this function to get a text string that describes the |
608 | error. |
609 | |
610 | \sa error() |
611 | */ |
612 | QString QSharedMemory::errorString() const |
613 | { |
614 | Q_D(const QSharedMemory); |
615 | return d->errorString; |
616 | } |
617 | |
618 | #endif // QT_NO_SHAREDMEMORY |
619 | |
620 | QT_END_NAMESPACE |
621 | |
622 | #ifndef QT_NO_QOBJECT |
623 | #include "moc_qsharedmemory.cpp" |
624 | #endif |
625 | |