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 "qsystemsemaphore.h" |
41 | #include "qsystemsemaphore_p.h" |
42 | |
43 | #include <qdebug.h> |
44 | #include <qfile.h> |
45 | #include <qcoreapplication.h> |
46 | |
47 | #ifndef QT_POSIX_IPC |
48 | |
49 | #ifndef QT_NO_SYSTEMSEMAPHORE |
50 | |
51 | #include <sys/types.h> |
52 | #include <sys/ipc.h> |
53 | #include <sys/sem.h> |
54 | #include <fcntl.h> |
55 | #include <errno.h> |
56 | |
57 | #include "private/qcore_unix_p.h" |
58 | |
59 | // OpenBSD 4.2 doesn't define EIDRM, see BUGS section: |
60 | // http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2 |
61 | #if defined(Q_OS_OPENBSD) && !defined(EIDRM) |
62 | #define EIDRM EINVAL |
63 | #endif |
64 | |
65 | QT_BEGIN_NAMESPACE |
66 | |
67 | /*! |
68 | \internal |
69 | |
70 | Setup unix_key |
71 | */ |
72 | key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode) |
73 | { |
74 | if (key.isEmpty()){ |
75 | errorString = |
76 | #if QT_CONFIG(translation) |
77 | QCoreApplication::tr(s: "%1: key is empty" , c: "QSystemSemaphore" ) |
78 | #else |
79 | QLatin1String("%1: key is empty" ) |
80 | #endif |
81 | .arg(a: QLatin1String("QSystemSemaphore::handle:" )); |
82 | error = QSystemSemaphore::KeyError; |
83 | return -1; |
84 | } |
85 | |
86 | // ftok requires that an actual file exists somewhere |
87 | if (-1 != unix_key) |
88 | return unix_key; |
89 | |
90 | // Create the file needed for ftok |
91 | int built = QSharedMemoryPrivate::createUnixKeyFile(fileName); |
92 | if (-1 == built) { |
93 | errorString = |
94 | #if QT_CONFIG(translation) |
95 | QCoreApplication::tr(s: "%1: unable to make key" , c: "QSystemSemaphore" ) |
96 | #else |
97 | QLatin1String("%1: unable to make key" ) |
98 | #endif |
99 | .arg(a: QLatin1String("QSystemSemaphore::handle:" )); |
100 | error = QSystemSemaphore::KeyError; |
101 | return -1; |
102 | } |
103 | createdFile = (1 == built); |
104 | |
105 | #if !defined(QT_NO_SHAREDMEMORY) && !defined(QT_POSIX_IPC) && !defined(Q_OS_ANDROID) |
106 | // Get the unix key for the created file |
107 | unix_key = ftok(pathname: QFile::encodeName(fileName).constData(), proj_id: 'Q'); |
108 | #endif |
109 | if (-1 == unix_key) { |
110 | errorString = |
111 | #if QT_CONFIG(translation) |
112 | QCoreApplication::tr(s: "%1: ftok failed" , c: "QSystemSemaphore" ) |
113 | #else |
114 | QLatin1String("%1: ftok failed" ) |
115 | #endif |
116 | .arg(a: QLatin1String("QSystemSemaphore::handle:" )); |
117 | error = QSystemSemaphore::KeyError; |
118 | return -1; |
119 | } |
120 | |
121 | // Get semaphore |
122 | semaphore = semget(key: unix_key, nsems: 1, semflg: 0600 | IPC_CREAT | IPC_EXCL); |
123 | if (-1 == semaphore) { |
124 | if (errno == EEXIST) |
125 | semaphore = semget(key: unix_key, nsems: 1, semflg: 0600 | IPC_CREAT); |
126 | if (-1 == semaphore) { |
127 | setErrorString(QLatin1String("QSystemSemaphore::handle" )); |
128 | cleanHandle(); |
129 | return -1; |
130 | } |
131 | } else { |
132 | createdSemaphore = true; |
133 | // Force cleanup of file, it is possible that it can be left over from a crash |
134 | createdFile = true; |
135 | } |
136 | |
137 | if (mode == QSystemSemaphore::Create) { |
138 | createdSemaphore = true; |
139 | createdFile = true; |
140 | } |
141 | |
142 | // Created semaphore so initialize its value. |
143 | if (createdSemaphore && initialValue >= 0) { |
144 | qt_semun init_op; |
145 | init_op.val = initialValue; |
146 | if (-1 == semctl(semid: semaphore, semnum: 0, SETVAL, init_op)) { |
147 | setErrorString(QLatin1String("QSystemSemaphore::handle" )); |
148 | cleanHandle(); |
149 | return -1; |
150 | } |
151 | } |
152 | |
153 | return unix_key; |
154 | } |
155 | |
156 | /*! |
157 | \internal |
158 | |
159 | Cleanup the unix_key |
160 | */ |
161 | void QSystemSemaphorePrivate::cleanHandle() |
162 | { |
163 | unix_key = -1; |
164 | |
165 | // remove the file if we made it |
166 | if (createdFile) { |
167 | QFile::remove(fileName); |
168 | createdFile = false; |
169 | } |
170 | |
171 | if (createdSemaphore) { |
172 | if (-1 != semaphore) { |
173 | if (-1 == semctl(semid: semaphore, semnum: 0, IPC_RMID, 0)) { |
174 | setErrorString(QLatin1String("QSystemSemaphore::cleanHandle" )); |
175 | #if defined QSYSTEMSEMAPHORE_DEBUG |
176 | qDebug("QSystemSemaphore::cleanHandle semctl failed." ); |
177 | #endif |
178 | } |
179 | semaphore = -1; |
180 | } |
181 | createdSemaphore = false; |
182 | } |
183 | } |
184 | |
185 | /*! |
186 | \internal |
187 | */ |
188 | bool QSystemSemaphorePrivate::modifySemaphore(int count) |
189 | { |
190 | if (-1 == handle()) |
191 | return false; |
192 | |
193 | struct sembuf operation; |
194 | operation.sem_num = 0; |
195 | operation.sem_op = count; |
196 | operation.sem_flg = SEM_UNDO; |
197 | |
198 | int res; |
199 | EINTR_LOOP(res, semop(semaphore, &operation, 1)); |
200 | if (-1 == res) { |
201 | // If the semaphore was removed be nice and create it and then modifySemaphore again |
202 | if (errno == EINVAL || errno == EIDRM) { |
203 | semaphore = -1; |
204 | cleanHandle(); |
205 | handle(); |
206 | return modifySemaphore(count); |
207 | } |
208 | setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore" )); |
209 | #if defined QSYSTEMSEMAPHORE_DEBUG |
210 | qDebug("QSystemSemaphore::modify failed %d %d %d %d %d" , |
211 | count, int(semctl(semaphore, 0, GETVAL)), int(errno), int(EIDRM), int(EINVAL); |
212 | #endif |
213 | return false; |
214 | } |
215 | |
216 | clearError(); |
217 | return true; |
218 | } |
219 | |
220 | |
221 | QT_END_NAMESPACE |
222 | |
223 | #endif // QT_NO_SYSTEMSEMAPHORE |
224 | |
225 | #endif // QT_POSIX_IPC |
226 | |