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 | |
32 | QT_BEGIN_NAMESPACE |
33 | |
34 | using namespace Qt::StringLiterals; |
35 | |
36 | bool 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 | */ |
54 | key_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 | */ |
139 | void 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 | */ |
166 | bool 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 | |
199 | QT_END_NAMESPACE |
200 | |
201 | #endif // QT_CONFIG(sysv_sem) |
202 | |