1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qsystemsemaphore.h"
6#include "qsystemsemaphore_p.h"
7
8#if QT_CONFIG(systemsemaphore)
9#include <QtCore/q20memory.h>
10#include <errno.h>
11
12QT_BEGIN_NAMESPACE
13
14using namespace QtIpcCommon;
15using namespace Qt::StringLiterals;
16
17inline void QSystemSemaphorePrivate::constructBackend()
18{
19 visit(lambda: [](auto p) { q20::construct_at(p); });
20}
21
22inline void QSystemSemaphorePrivate::destructBackend()
23{
24 visit(lambda: [](auto p) { std::destroy_at(p); });
25}
26
27/*!
28 \class QSystemSemaphore
29 \inmodule QtCore
30 \since 4.4
31
32 \brief The QSystemSemaphore class provides a general counting system semaphore.
33
34 A system semaphore is a generalization of \l QSemaphore. Typically, a
35 semaphore is used to protect a certain number of identical resources.
36
37 Like its lighter counterpart, a QSystemSemaphore can be
38 accessed from multiple \l {QThread} {threads}. Unlike QSemaphore, a
39 QSystemSemaphore can also be accessed from multiple \l {QProcess}
40 {processes}. This means QSystemSemaphore is a much heavier class, so
41 if your application doesn't need to access your semaphores across
42 multiple processes, you will probably want to use QSemaphore.
43
44 Semaphores support two fundamental operations, acquire() and release():
45
46 acquire() tries to acquire one resource. If there isn't a resource
47 available, the call blocks until a resource becomes available. Then
48 the resource is acquired and the call returns.
49
50 release() releases one resource so it can be acquired by another
51 process. The function can also be called with a parameter n > 1,
52 which releases n resources.
53
54 System semaphores are identified by a key, represented by \l QNativeIpcKey. A
55 key can be created in a cross-platform manner by using platformSafeKey(). A
56 system semaphore is created by the QSystemSemaphore constructor when passed
57 an access mode parameter of AccessMode::Create. Once it is created, other
58 processes may attach to the same semaphore using the same key and an access
59 mode parameter of AccessMode::Open.
60
61 Example: Create a system semaphore
62 \snippet code/src_corelib_kernel_qsystemsemaphore.cpp 0
63
64 For details on the key types, platform-specific limitations, and
65 interoperability with older or non-Qt applications, see the \l{Native IPC
66 Keys} documentation. That includes important information for sandboxed
67 applications on Apple platforms, including all apps obtained via the Apple
68 App Store.
69
70 \sa {Inter-Process Communication}, QSharedMemory, QSemaphore
71 */
72
73/*!
74 Requests a system semaphore identified by the legacy key \a key.
75 */
76QSystemSemaphore::QSystemSemaphore(const QString &key, int initialValue, AccessMode mode)
77 : QSystemSemaphore(legacyNativeKey(key), initialValue, mode)
78{
79}
80
81/*!
82 Requests a system semaphore for the specified \a key. The parameters
83 \a initialValue and \a mode are used according to the following
84 rules, which are system dependent.
85
86 In Unix, if the \a mode is \l {QSystemSemaphore::} {Open} and the
87 system already has a semaphore identified by \a key, that semaphore
88 is used, and the semaphore's resource count is not changed, i.e., \a
89 initialValue is ignored. But if the system does not already have a
90 semaphore identified by \a key, it creates a new semaphore for that
91 key and sets its resource count to \a initialValue.
92
93 In Unix, if the \a mode is \l {QSystemSemaphore::} {Create} and the
94 system already has a semaphore identified by \a key, that semaphore
95 is used, and its resource count is set to \a initialValue. If the
96 system does not already have a semaphore identified by \a key, it
97 creates a new semaphore for that key and sets its resource count to
98 \a initialValue.
99
100 In Windows, \a mode is ignored, and the system always tries to
101 create a semaphore for the specified \a key. If the system does not
102 already have a semaphore identified as \a key, it creates the
103 semaphore and sets its resource count to \a initialValue. But if the
104 system already has a semaphore identified as \a key it uses that
105 semaphore and ignores \a initialValue.
106
107 The \l {QSystemSemaphore::AccessMode} {mode} parameter is only used
108 in Unix systems to handle the case where a semaphore survives a
109 process crash. In that case, the next process to allocate a
110 semaphore with the same \a key will get the semaphore that survived
111 the crash, and unless \a mode is \l {QSystemSemaphore::} {Create},
112 the resource count will not be reset to \a initialValue but will
113 retain the initial value it had been given by the crashed process.
114
115 \sa acquire(), key()
116 */
117QSystemSemaphore::QSystemSemaphore(const QNativeIpcKey &key, int initialValue, AccessMode mode)
118 : d(new QSystemSemaphorePrivate(key.type()))
119{
120 setNativeKey(key, initialValue, mode);
121}
122
123/*!
124 The destructor destroys the QSystemSemaphore object, but the
125 underlying system semaphore is not removed from the system unless
126 this instance of QSystemSemaphore is the last one existing for that
127 system semaphore.
128
129 Two important side effects of the destructor depend on the system.
130 In Windows, if acquire() has been called for this semaphore but not
131 release(), release() will not be called by the destructor, nor will
132 the resource be released when the process exits normally. This would
133 be a program bug which could be the cause of a deadlock in another
134 process trying to acquire the same resource. In Unix, acquired
135 resources that are not released before the destructor is called are
136 automatically released when the process exits.
137*/
138QSystemSemaphore::~QSystemSemaphore()
139{
140 d->cleanHandle();
141}
142
143/*!
144 \enum QSystemSemaphore::AccessMode
145
146 This enum is used by the constructor and setKey(). Its purpose is to
147 enable handling the problem in Unix implementations of semaphores
148 that survive a crash. In Unix, when a semaphore survives a crash, we
149 need a way to force it to reset its resource count, when the system
150 reuses the semaphore. In Windows, where semaphores can't survive a
151 crash, this enum has no effect.
152
153 \value Open If the semaphore already exists, its initial resource
154 count is not reset. If the semaphore does not already exist, it is
155 created and its initial resource count set.
156
157 \value Create QSystemSemaphore takes ownership of the semaphore and
158 sets its resource count to the requested value, regardless of
159 whether the semaphore already exists by having survived a crash.
160 This value should be passed to the constructor, when the first
161 semaphore for a particular key is constructed and you know that if
162 the semaphore already exists it could only be because of a crash. In
163 Windows, where a semaphore can't survive a crash, Create and Open
164 have the same behavior.
165*/
166
167/*!
168 This function works the same as the constructor. It reconstructs
169 this QSystemSemaphore object. If the new \a key is different from
170 the old key, calling this function is like calling the destructor of
171 the semaphore with the old key, then calling the constructor to
172 create a new semaphore with the new \a key. The \a initialValue and
173 \a mode parameters are as defined for the constructor.
174
175 This function is useful if the native key was shared from another process.
176 See \l{Native IPC Keys} for more information.
177
178 \sa QSystemSemaphore(), nativeIpcKey()
179 */
180void QSystemSemaphore::setNativeKey(const QNativeIpcKey &key, int initialValue, AccessMode mode)
181{
182 if (key == d->nativeKey && mode == Open)
183 return;
184 if (!isKeyTypeSupported(type: key.type())) {
185 d->setError(e: KeyError, message: tr(sourceText: "%1: unsupported key type")
186 .arg(a: "QSystemSemaphore::setNativeKey"_L1));
187 return;
188 }
189
190 d->clearError();
191 d->cleanHandle();
192 if (key.type() == d->nativeKey.type()) {
193 // we can reuse the backend
194 d->nativeKey = key;
195 } else {
196 // we must recreate the backend
197 d->destructBackend();
198 d->nativeKey = key;
199 d->constructBackend();
200 }
201 d->initialValue = initialValue;
202 d->handle(mode);
203}
204
205/*!
206 Returns the key assigned to this system semaphore. The key is the
207 name by which the semaphore can be accessed from other processes.
208
209 You can use the native key to access system semaphores that have not been
210 created by Qt, or to grant access to non-Qt applications. See \l{Native IPC
211 Keys} for more information.
212
213 \sa setNativeKey()
214 */
215QNativeIpcKey QSystemSemaphore::nativeIpcKey() const
216{
217 return d->nativeKey;
218}
219
220/*!
221 This function works the same as the constructor. It reconstructs
222 this QSystemSemaphore object. If the new \a key is different from
223 the old key, calling this function is like calling the destructor of
224 the semaphore with the old key, then calling the constructor to
225 create a new semaphore with the new \a key. The \a initialValue and
226 \a mode parameters are as defined for the constructor.
227
228 \sa QSystemSemaphore(), key()
229 */
230void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode mode)
231{
232 setNativeKey(key: legacyNativeKey(key), initialValue, mode);
233}
234
235/*!
236 Returns the legacy key assigned to this system semaphore. The key is the
237 name by which the semaphore can be accessed from other processes.
238
239 \sa setKey()
240 */
241QString QSystemSemaphore::key() const
242{
243 return QNativeIpcKeyPrivate::legacyKey(key: d->nativeKey);
244}
245
246/*!
247 Acquires one of the resources guarded by this semaphore, if there is
248 one available, and returns \c true. If all the resources guarded by this
249 semaphore have already been acquired, the call blocks until one of
250 them is released by another process or thread having a semaphore
251 with the same key.
252
253 If false is returned, a system error has occurred. Call error()
254 to get a value of QSystemSemaphore::SystemSemaphoreError that
255 indicates which error occurred.
256
257 \sa release()
258 */
259bool QSystemSemaphore::acquire()
260{
261 return d->modifySemaphore(count: -1);
262}
263
264/*!
265 Releases \a n resources guarded by the semaphore. Returns \c true
266 unless there is a system error.
267
268 Example: Create a system semaphore having five resources; acquire
269 them all and then release them all.
270
271 \snippet code/src_corelib_kernel_qsystemsemaphore.cpp 1
272
273 This function can also "create" resources. For example, immediately
274 following the sequence of statements above, suppose we add the
275 statement:
276
277 \snippet code/src_corelib_kernel_qsystemsemaphore.cpp 2
278
279 Ten new resources are now guarded by the semaphore, in addition to
280 the five that already existed. You would not normally use this
281 function to create more resources.
282
283 \sa acquire()
284 */
285bool QSystemSemaphore::release(int n)
286{
287 if (n == 0)
288 return true;
289 if (n < 0) {
290 qWarning(msg: "QSystemSemaphore::release: n is negative.");
291 return false;
292 }
293 return d->modifySemaphore(count: n);
294}
295
296/*!
297 Returns a value indicating whether an error occurred, and, if so,
298 which error it was.
299
300 \sa errorString()
301 */
302QSystemSemaphore::SystemSemaphoreError QSystemSemaphore::error() const
303{
304 return d->error;
305}
306
307/*!
308 \enum QSystemSemaphore::SystemSemaphoreError
309
310 \value NoError No error occurred.
311
312 \value PermissionDenied The operation failed because the caller
313 didn't have the required permissions.
314
315 \value KeyError The operation failed because of an invalid key.
316
317 \value AlreadyExists The operation failed because a system
318 semaphore with the specified key already existed.
319
320 \value NotFound The operation failed because a system semaphore
321 with the specified key could not be found.
322
323 \value OutOfResources The operation failed because there was
324 not enough memory available to fill the request.
325
326 \value UnknownError Something else happened and it was bad.
327*/
328
329/*!
330 Returns a text description of the last error that occurred. If
331 error() returns an \l {QSystemSemaphore::SystemSemaphoreError} {error
332 value}, call this function to get a text string that describes the
333 error.
334
335 \sa error()
336 */
337QString QSystemSemaphore::errorString() const
338{
339 return d->errorString;
340}
341
342void QSystemSemaphorePrivate::setUnixErrorString(QLatin1StringView function)
343{
344 // EINVAL is handled in functions so they can give better error strings
345 switch (errno) {
346 case EPERM:
347 case EACCES:
348 errorString = QSystemSemaphore::tr(sourceText: "%1: permission denied").arg(a: function);
349 error = QSystemSemaphore::PermissionDenied;
350 break;
351 case EEXIST:
352 errorString = QSystemSemaphore::tr(sourceText: "%1: already exists").arg(a: function);
353 error = QSystemSemaphore::AlreadyExists;
354 break;
355 case ENOENT:
356 errorString = QSystemSemaphore::tr(sourceText: "%1: does not exist").arg(a: function);
357 error = QSystemSemaphore::NotFound;
358 break;
359 case ERANGE:
360 case ENOSPC:
361 case EMFILE:
362 errorString = QSystemSemaphore::tr(sourceText: "%1: out of resources").arg(a: function);
363 error = QSystemSemaphore::OutOfResources;
364 break;
365 case ENAMETOOLONG:
366 errorString = QSystemSemaphore::tr(sourceText: "%1: key too long").arg(a: function);
367 error = QSystemSemaphore::KeyError;
368 break;
369 default:
370 errorString = QSystemSemaphore::tr(sourceText: "%1: unknown error: %2")
371 .arg(args&: function, args: qt_error_string(errno));
372 error = QSystemSemaphore::UnknownError;
373#if defined QSYSTEMSEMAPHORE_DEBUG
374 qDebug() << errorString << "key" << key << "errno" << errno << EINVAL;
375#endif
376 }
377}
378
379bool QSystemSemaphore::isKeyTypeSupported(QNativeIpcKey::Type type)
380{
381 if (!isIpcSupported(ipcType: IpcType::SystemSemaphore, type))
382 return false;
383 using Variant = decltype(QSystemSemaphorePrivate::backend);
384 return Variant::staticVisit(keyType: type, lambda: [](auto ptr) {
385 using Impl = std::decay_t<decltype(*ptr)>;
386 return Impl::runtimeSupportCheck();
387 });
388}
389
390QNativeIpcKey QSystemSemaphore::platformSafeKey(const QString &key, QNativeIpcKey::Type type)
391{
392 return QtIpcCommon::platformSafeKey(key, ipcType: IpcType::SystemSemaphore, type);
393}
394
395QNativeIpcKey QSystemSemaphore::legacyNativeKey(const QString &key, QNativeIpcKey::Type type)
396{
397 return QtIpcCommon::legacyPlatformSafeKey(key, ipcType: IpcType::SystemSemaphore, type);
398}
399
400QT_END_NAMESPACE
401
402#include "moc_qsystemsemaphore.cpp"
403
404#endif // QT_CONFIG(systemsemaphore)
405

source code of qtbase/src/corelib/ipc/qsystemsemaphore.cpp