1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2022 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qsystemsemaphore.h"
6#include "qsystemsemaphore_p.h"
7
8#include <qdebug.h>
9#include <qfile.h>
10#include <qcoreapplication.h>
11
12#if QT_CONFIG(sysv_sem)
13
14#include <sys/types.h>
15#include <sys/ipc.h>
16#include <sys/sem.h>
17#include <fcntl.h>
18#include <errno.h>
19
20#if defined(Q_OS_DARWIN)
21#include "private/qcore_mac_p.h"
22#endif
23
24#include "private/qcore_unix_p.h"
25
26// OpenBSD 4.2 doesn't define EIDRM, see BUGS section:
27// http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2
28#if defined(Q_OS_OPENBSD) && !defined(EIDRM)
29#define EIDRM EINVAL
30#endif
31
32QT_BEGIN_NAMESPACE
33
34using namespace Qt::StringLiterals;
35
36bool QSystemSemaphoreSystemV::runtimeSupportCheck()
37{
38#if defined(Q_OS_DARWIN)
39 if (qt_apple_isSandboxed())
40 return false;
41#endif
42 static const bool result = []() {
43 (void)semget(IPC_PRIVATE, nsems: -1, semflg: 0); // this will fail
44 return errno != ENOSYS;
45 }();
46 return result;
47}
48
49/*!
50 \internal
51
52 Setup unix_key
53 */
54key_t QSystemSemaphoreSystemV::handle(QSystemSemaphorePrivate *self, QSystemSemaphore::AccessMode mode)
55{
56 if (unix_key != -1)
57 return unix_key; // we already have a semaphore
58
59#if defined(Q_OS_DARWIN)
60 if (qt_apple_isSandboxed()) {
61 // attempting to use System V semaphores will get us a SIGSYS
62 self->setError(QSystemSemaphore::PermissionDenied,
63 QSystemSemaphore::tr("%1: System V semaphores are not available for "
64 "sandboxed applications. Please build Qt with "
65 "-feature-ipc_posix")
66 .arg("QSystemSemaphore::handle:"_L1));
67 return -1;
68 }
69#endif
70
71 nativeKeyFile = QFile::encodeName(fileName: self->nativeKey.nativeKey());
72 if (nativeKeyFile.isEmpty()) {
73 self->setError(e: QSystemSemaphore::KeyError,
74 message: QSystemSemaphore::tr(sourceText: "%1: key is empty")
75 .arg(a: "QSystemSemaphore::handle:"_L1));
76 return -1;
77 }
78
79 // ftok requires that an actual file exists somewhere
80 int built = QtIpcCommon::createUnixKeyFile(fileName: nativeKeyFile);
81 if (-1 == built) {
82 self->setError(e: QSystemSemaphore::KeyError,
83 message: QSystemSemaphore::tr(sourceText: "%1: unable to make key")
84 .arg(a: "QSystemSemaphore::handle:"_L1));
85
86 return -1;
87 }
88 createdFile = (1 == built);
89
90 // Get the unix key for the created file
91 unix_key = ftok(pathname: nativeKeyFile, proj_id: int(self->nativeKey.type()));
92 if (-1 == unix_key) {
93 self->setError(e: QSystemSemaphore::KeyError,
94 message: QSystemSemaphore::tr(sourceText: "%1: ftok failed")
95 .arg(a: "QSystemSemaphore::handle:"_L1));
96 return -1;
97 }
98
99 // Get semaphore
100 semaphore = semget(key: unix_key, nsems: 1, semflg: 0600 | IPC_CREAT | IPC_EXCL);
101 if (-1 == semaphore) {
102 if (errno == EEXIST)
103 semaphore = semget(key: unix_key, nsems: 1, semflg: 0600 | IPC_CREAT);
104 if (-1 == semaphore) {
105 self->setUnixErrorString("QSystemSemaphore::handle"_L1);
106 cleanHandle(self);
107 return -1;
108 }
109 } else {
110 createdSemaphore = true;
111 // Force cleanup of file, it is possible that it can be left over from a crash
112 createdFile = true;
113 }
114
115 if (mode == QSystemSemaphore::Create) {
116 createdSemaphore = true;
117 createdFile = true;
118 }
119
120 // Created semaphore so initialize its value.
121 if (createdSemaphore && self->initialValue >= 0) {
122 qt_semun init_op;
123 init_op.val = self->initialValue;
124 if (-1 == semctl(semid: semaphore, semnum: 0, SETVAL, init_op)) {
125 self->setUnixErrorString("QSystemSemaphore::handle"_L1);
126 cleanHandle(self);
127 return -1;
128 }
129 }
130
131 return unix_key;
132}
133
134/*!
135 \internal
136
137 Cleanup the unix_key
138 */
139void QSystemSemaphoreSystemV::cleanHandle(QSystemSemaphorePrivate *self)
140{
141 unix_key = -1;
142
143 // remove the file if we made it
144 if (createdFile) {
145 unlink(name: nativeKeyFile.constData());
146 createdFile = false;
147 }
148
149 if (createdSemaphore) {
150 if (-1 != semaphore) {
151 if (-1 == semctl(semid: semaphore, semnum: 0, IPC_RMID, 0)) {
152 self->setUnixErrorString("QSystemSemaphore::cleanHandle"_L1);
153#if defined QSYSTEMSEMAPHORE_DEBUG
154 qDebug("QSystemSemaphoreSystemV::cleanHandle semctl failed.");
155#endif
156 }
157 semaphore = -1;
158 }
159 createdSemaphore = false;
160 }
161}
162
163/*!
164 \internal
165 */
166bool QSystemSemaphoreSystemV::modifySemaphore(QSystemSemaphorePrivate *self, int count)
167{
168 if (handle(self, mode: QSystemSemaphore::Open) == -1)
169 return false;
170
171 struct sembuf operation;
172 operation.sem_num = 0;
173 operation.sem_op = count;
174 operation.sem_flg = SEM_UNDO;
175
176 int res;
177 EINTR_LOOP(res, semop(semaphore, &operation, 1));
178 if (-1 == res) {
179 // If the semaphore was removed be nice and create it and then modifySemaphore again
180 if (errno == EINVAL || errno == EIDRM) {
181 semaphore = -1;
182 cleanHandle(self);
183 handle(self, mode: QSystemSemaphore::Open);
184 return modifySemaphore(self, count);
185 }
186 self->setUnixErrorString("QSystemSemaphore::modifySemaphore"_L1);
187#if defined QSYSTEMSEMAPHORE_DEBUG
188 qDebug("QSystemSemaphoreSystemV::modify failed %d %d %d %d %d",
189 count, int(semctl(semaphore, 0, GETVAL)), int(errno), int(EIDRM), int(EINVAL);
190#endif
191 return false;
192 }
193
194 self->clearError();
195 return true;
196}
197
198
199QT_END_NAMESPACE
200
201#endif // QT_CONFIG(sysv_sem)
202

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