1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsystemsemaphore.h"
43 #include "qsystemsemaphore_p.h"
44
45 #include <qcoreapplication.h>
46 #include <qdebug.h>
47 #include <qfile.h>
48
49 #ifndef QT_NO_SYSTEMSEMAPHORE
50
51 #include <sys/types.h>
52 #include <sys/ipc.h>
53 #ifndef QT_POSIX_IPC
54 #include <sys/sem.h>
55 #endif
56 #include <fcntl.h>
57 #include <errno.h>
58
59 #include "private/qcore_unix_p.h"
60
61 // OpenBSD 4.2 doesn't define EIDRM, see BUGS section:
62 // http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2
63 #if defined(Q_OS_OPENBSD) && !defined(EIDRM)
64 #define EIDRM EINVAL
65 #endif
66
67 //#define QSYSTEMSEMAPHORE_DEBUG
68
69 QT_BEGIN_NAMESPACE
70
QSystemSemaphorePrivate()71 QSystemSemaphorePrivate::QSystemSemaphorePrivate() :
72 #ifndef QT_POSIX_IPC
73 unix_key(-1), semaphore(-1), createdFile(false),
74 #else
75 semaphore(SEM_FAILED),
76 #endif
77 createdSemaphore(false), error(QSystemSemaphore::NoError)
78 {
79 }
80
setErrorString(const QString & function)81 void QSystemSemaphorePrivate::setErrorString(const QString &function)
82 {
83 // EINVAL is handled in functions so they can give better error strings
84 switch (errno) {
85 case EPERM:
86 case EACCES:
87 errorString = QCoreApplication::translate("QSystemSemaphore", "%1: permission denied").arg(function);
88 error = QSystemSemaphore::PermissionDenied;
89 break;
90 case EEXIST:
91 errorString = QCoreApplication::translate("QSystemSemaphore", "%1: already exists").arg(function);
92 error = QSystemSemaphore::AlreadyExists;
93 break;
94 case ENOENT:
95 errorString = QCoreApplication::translate("QSystemSemaphore", "%1: does not exist").arg(function);
96 error = QSystemSemaphore::NotFound;
97 break;
98 case ERANGE:
99 case ENOMEM:
100 case ENOSPC:
101 case EMFILE:
102 case ENFILE:
103 case EOVERFLOW:
104 errorString = QCoreApplication::translate("QSystemSemaphore", "%1: out of resources").arg(function);
105 error = QSystemSemaphore::OutOfResources;
106 break;
107 case ENAMETOOLONG:
108 errorString = QCoreApplication::translate("QSystemSemaphore", "%1: name error").arg(function);
109 error = QSystemSemaphore::KeyError;
110 break;
111 default:
112 errorString = QCoreApplication::translate("QSystemSemaphore", "%1: unknown error %2").arg(function).arg(errno);
113 error = QSystemSemaphore::UnknownError;
114 #ifdef QSYSTEMSEMAPHORE_DEBUG
115 qDebug() << errorString << "key" << key << "errno" << errno << EINVAL;
116 #endif
117 break;
118 }
119 }
120
121 /*!
122 \internal
123
124 Initialise the semaphore
125 */
126 #ifndef QT_POSIX_IPC
handle(QSystemSemaphore::AccessMode mode)127 key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode)
128 {
129 if (-1 != unix_key)
130 return unix_key;
131
132 if (key.isEmpty()) {
133 errorString = QCoreApplication::tr("%1: key is empty", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle"));
134 error = QSystemSemaphore::KeyError;
135 return -1;
136 }
137
138 // ftok requires that an actual file exists somewhere
139 int built = QSharedMemoryPrivate::createUnixKeyFile(fileName);
140 if (-1 == built) {
141 errorString = QCoreApplication::tr("%1: unable to make key", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle"));
142 error = QSystemSemaphore::KeyError;
143 return -1;
144 }
145 createdFile = (1 == built);
146
147 // Get the unix key for the created file
148 unix_key = ftok(QFile::encodeName(fileName).constData(), 'Q');
149 if (-1 == unix_key) {
150 errorString = QCoreApplication::tr("%1: ftok failed", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle"));
151 error = QSystemSemaphore::KeyError;
152 return -1;
153 }
154
155 // Get semaphore
156 semaphore = semget(unix_key, 1, 0600 | IPC_CREAT | IPC_EXCL);
157 if (-1 == semaphore) {
158 if (errno == EEXIST)
159 semaphore = semget(unix_key, 1, 0600 | IPC_CREAT);
160 if (-1 == semaphore) {
161 setErrorString(QLatin1String("QSystemSemaphore::handle"));
162 cleanHandle();
163 return -1;
164 }
165 if (mode == QSystemSemaphore::Create) {
166 createdSemaphore = true;
167 createdFile = true;
168 }
169 } else {
170 createdSemaphore = true;
171 // Force cleanup of file, it is possible that it can be left over from a crash
172 createdFile = true;
173 }
174
175 // Created semaphore so initialize its value.
176 if (createdSemaphore && initialValue >= 0) {
177 qt_semun init_op;
178 init_op.val = initialValue;
179 if (-1 == semctl(semaphore, 0, SETVAL, init_op)) {
180 setErrorString(QLatin1String("QSystemSemaphore::handle"));
181 cleanHandle();
182 return -1;
183 }
184 }
185
186 return unix_key;
187 }
188 #else
handle(QSystemSemaphore::AccessMode mode)189 bool QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode)
190 {
191 if (semaphore != SEM_FAILED)
192 return true; // we already have a semaphore
193
194 if (fileName.isEmpty()) {
195 errorString = QCoreApplication::tr("%1: key is empty", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle"));
196 error = QSystemSemaphore::KeyError;
197 return false;
198 }
199
200 QByteArray semName = QFile::encodeName(fileName);
201
202 // Always try with O_EXCL so we know whether we created the semaphore.
203 int oflag = O_CREAT | O_EXCL;
204 for (int tryNum = 0, maxTries = 1; tryNum < maxTries; ++tryNum) {
205 do {
206 semaphore = sem_open(semName.constData(), oflag, 0666, initialValue);
207 } while (semaphore == SEM_FAILED && errno == EINTR);
208 if (semaphore == SEM_FAILED && errno == EEXIST) {
209 if (mode == QSystemSemaphore::Create) {
210 if (sem_unlink(semName.constData()) == -1 && errno != ENOENT) {
211 setErrorString(QLatin1String("QSystemSemaphore::handle (sem_unlink)"));
212 return false;
213 }
214 // Race condition: the semaphore might be recreated before
215 // we call sem_open again, so we'll retry several times.
216 maxTries = 3;
217 } else {
218 // Race condition: if it no longer exists at the next sem_open
219 // call, we won't realize we created it, so we'll leak it later.
220 oflag &= ~O_EXCL;
221 maxTries = 2;
222 }
223 } else {
224 break;
225 }
226 }
227 if (semaphore == SEM_FAILED) {
228 setErrorString(QLatin1String("QSystemSemaphore::handle"));
229 return false;
230 }
231
232 createdSemaphore = (oflag & O_EXCL) != 0;
233 return true;
234 }
235 #endif // QT_POSIX_IPC
236
237 /*!
238 \internal
239
240 Clean up the semaphore
241 */
cleanHandle()242 void QSystemSemaphorePrivate::cleanHandle()
243 {
244 #ifndef QT_POSIX_IPC
245 unix_key = -1;
246
247 // remove the file if we made it
248 if (createdFile) {
249 QFile::remove(fileName);
250 createdFile = false;
251 }
252
253 if (createdSemaphore) {
254 if (-1 != semaphore) {
255 if (-1 == semctl(semaphore, 0, IPC_RMID, 0)) {
256 setErrorString(QLatin1String("QSystemSemaphore::cleanHandle"));
257 #ifdef QSYSTEMSEMAPHORE_DEBUG
258 qDebug("QSystemSemaphore::cleanHandle semctl failed.");
259 #endif
260 }
261 semaphore = -1;
262 }
263 createdSemaphore = false;
264 }
265 #else
266 if (semaphore != SEM_FAILED) {
267 if (sem_close(semaphore) == -1) {
268 setErrorString(QLatin1String("QSystemSemaphore::cleanHandle (sem_close)"));
269 #ifdef QSYSTEMSEMAPHORE_DEBUG
270 qDebug() << QLatin1String("QSystemSemaphore::cleanHandle sem_close failed.");
271 #endif
272 }
273 semaphore = SEM_FAILED;
274 }
275
276 if (createdSemaphore) {
277 if (sem_unlink(QFile::encodeName(fileName).constData()) == -1 && errno != ENOENT) {
278 setErrorString(QLatin1String("QSystemSemaphore::cleanHandle (sem_unlink)"));
279 #ifdef QSYSTEMSEMAPHORE_DEBUG
280 qDebug() << QLatin1String("QSystemSemaphore::cleanHandle sem_unlink failed.");
281 #endif
282 }
283 createdSemaphore = false;
284 }
285 #endif // QT_POSIX_IPC
286 }
287
288 /*!
289 \internal
290 */
modifySemaphore(int count)291 bool QSystemSemaphorePrivate::modifySemaphore(int count)
292 {
293 #ifndef QT_POSIX_IPC
294 if (-1 == handle())
295 return false;
296
297 struct sembuf operation;
298 operation.sem_num = 0;
299 operation.sem_op = count;
300 operation.sem_flg = SEM_UNDO;
301
302 int res;
303 EINTR_LOOP(res, semop(semaphore, &operation, 1));
304 if (-1 == res) {
305 // If the semaphore was removed be nice and create it and then modifySemaphore again
306 if (errno == EINVAL || errno == EIDRM) {
307 semaphore = -1;
308 cleanHandle();
309 handle();
310 return modifySemaphore(count);
311 }
312 setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore"));
313 #ifdef QSYSTEMSEMAPHORE_DEBUG
314 qDebug() << QLatin1String("QSystemSemaphore::modify failed") << count << semctl(semaphore, 0, GETVAL) << errno << EIDRM << EINVAL;
315 #endif
316 return false;
317 }
318 #else
319 if (!handle())
320 return false;
321
322 if (count > 0) {
323 int cnt = count;
324 do {
325 if (sem_post(semaphore) == -1) {
326 setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore (sem_post)"));
327 #ifdef QSYSTEMSEMAPHORE_DEBUG
328 qDebug() << QLatin1String("QSystemSemaphore::modify sem_post failed") << count << errno;
329 #endif
330 // rollback changes to preserve the SysV semaphore behavior
331 for ( ; cnt < count; ++cnt) {
332 register int res;
333 EINTR_LOOP(res, sem_wait(semaphore));
334 }
335 return false;
336 }
337 --cnt;
338 } while (cnt > 0);
339 } else {
340 register int res;
341 EINTR_LOOP(res, sem_wait(semaphore));
342 if (res == -1) {
343 // If the semaphore was removed be nice and create it and then modifySemaphore again
344 if (errno == EINVAL || errno == EIDRM) {
345 semaphore = SEM_FAILED;
346 return modifySemaphore(count);
347 }
348 setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore (sem_wait)"));
349 #ifdef QSYSTEMSEMAPHORE_DEBUG
350 qDebug() << QLatin1String("QSystemSemaphore::modify sem_wait failed") << count << errno;
351 #endif
352 return false;
353 }
354 }
355 #endif // QT_POSIX_IPC
356
357 return true;
358 }
359
360 QT_END_NAMESPACE
361
362 #endif // QT_NO_SYSTEMSEMAPHORE
363