diff options
Diffstat (limited to 'src/corelib/ipc/qsystemsemaphore_systemv.cpp')
-rw-r--r-- | src/corelib/ipc/qsystemsemaphore_systemv.cpp | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/src/corelib/ipc/qsystemsemaphore_systemv.cpp b/src/corelib/ipc/qsystemsemaphore_systemv.cpp new file mode 100644 index 0000000000..e5d231d1d4 --- /dev/null +++ b/src/corelib/ipc/qsystemsemaphore_systemv.cpp @@ -0,0 +1,201 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2022 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qsystemsemaphore.h" +#include "qsystemsemaphore_p.h" + +#include <qdebug.h> +#include <qfile.h> +#include <qcoreapplication.h> + +#if QT_CONFIG(sysv_sem) + +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/sem.h> +#include <fcntl.h> +#include <errno.h> + +#if defined(Q_OS_DARWIN) +#include "private/qcore_mac_p.h" +#endif + +#include "private/qcore_unix_p.h" + +// OpenBSD 4.2 doesn't define EIDRM, see BUGS section: +// http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2 +#if defined(Q_OS_OPENBSD) && !defined(EIDRM) +#define EIDRM EINVAL +#endif + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +bool QSystemSemaphoreSystemV::runtimeSupportCheck() +{ +#if defined(Q_OS_DARWIN) + if (qt_apple_isSandboxed()) + return false; +#endif + static const bool result = []() { + (void)semget(IPC_PRIVATE, -1, 0); // this will fail + return errno != ENOSYS; + }(); + return result; +} + +/*! + \internal + + Setup unix_key + */ +key_t QSystemSemaphoreSystemV::handle(QSystemSemaphorePrivate *self, QSystemSemaphore::AccessMode mode) +{ + if (unix_key != -1) + return unix_key; // we already have a semaphore + +#if defined(Q_OS_DARWIN) + if (qt_apple_isSandboxed()) { + // attempting to use System V semaphores will get us a SIGSYS + self->setError(QSystemSemaphore::PermissionDenied, + QSystemSemaphore::tr("%1: System V semaphores are not available for " + "sandboxed applications. Please build Qt with " + "-feature-ipc_posix") + .arg("QSystemSemaphore::handle:"_L1)); + return -1; + } +#endif + + nativeKeyFile = QFile::encodeName(self->nativeKey.nativeKey()); + if (nativeKeyFile.isEmpty()) { + self->setError(QSystemSemaphore::KeyError, + QSystemSemaphore::tr("%1: key is empty") + .arg("QSystemSemaphore::handle:"_L1)); + return -1; + } + + // ftok requires that an actual file exists somewhere + int built = QtIpcCommon::createUnixKeyFile(nativeKeyFile); + if (-1 == built) { + self->setError(QSystemSemaphore::KeyError, + QSystemSemaphore::tr("%1: unable to make key") + .arg("QSystemSemaphore::handle:"_L1)); + + return -1; + } + createdFile = (1 == built); + + // Get the unix key for the created file + unix_key = ftok(nativeKeyFile, int(self->nativeKey.type())); + if (-1 == unix_key) { + self->setError(QSystemSemaphore::KeyError, + QSystemSemaphore::tr("%1: ftok failed") + .arg("QSystemSemaphore::handle:"_L1)); + return -1; + } + + // Get semaphore + semaphore = semget(unix_key, 1, 0600 | IPC_CREAT | IPC_EXCL); + if (-1 == semaphore) { + if (errno == EEXIST) + semaphore = semget(unix_key, 1, 0600 | IPC_CREAT); + if (-1 == semaphore) { + self->setUnixErrorString("QSystemSemaphore::handle"_L1); + cleanHandle(self); + return -1; + } + } else { + createdSemaphore = true; + // Force cleanup of file, it is possible that it can be left over from a crash + createdFile = true; + } + + if (mode == QSystemSemaphore::Create) { + createdSemaphore = true; + createdFile = true; + } + + // Created semaphore so initialize its value. + if (createdSemaphore && self->initialValue >= 0) { + qt_semun init_op; + init_op.val = self->initialValue; + if (-1 == semctl(semaphore, 0, SETVAL, init_op)) { + self->setUnixErrorString("QSystemSemaphore::handle"_L1); + cleanHandle(self); + return -1; + } + } + + return unix_key; +} + +/*! + \internal + + Cleanup the unix_key + */ +void QSystemSemaphoreSystemV::cleanHandle(QSystemSemaphorePrivate *self) +{ + unix_key = -1; + + // remove the file if we made it + if (createdFile) { + unlink(nativeKeyFile.constData()); + createdFile = false; + } + + if (createdSemaphore) { + if (-1 != semaphore) { + if (-1 == semctl(semaphore, 0, IPC_RMID, 0)) { + self->setUnixErrorString("QSystemSemaphore::cleanHandle"_L1); +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug("QSystemSemaphoreSystemV::cleanHandle semctl failed."); +#endif + } + semaphore = -1; + } + createdSemaphore = false; + } +} + +/*! + \internal + */ +bool QSystemSemaphoreSystemV::modifySemaphore(QSystemSemaphorePrivate *self, int count) +{ + if (handle(self, QSystemSemaphore::Open) == -1) + return false; + + struct sembuf operation; + operation.sem_num = 0; + operation.sem_op = count; + operation.sem_flg = SEM_UNDO; + + int res; + QT_EINTR_LOOP(res, semop(semaphore, &operation, 1)); + if (-1 == res) { + // If the semaphore was removed be nice and create it and then modifySemaphore again + if (errno == EINVAL || errno == EIDRM) { + semaphore = -1; + cleanHandle(self); + handle(self, QSystemSemaphore::Open); + return modifySemaphore(self, count); + } + self->setUnixErrorString("QSystemSemaphore::modifySemaphore"_L1); +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug("QSystemSemaphoreSystemV::modify failed %d %d %d %d %d", + count, int(semctl(semaphore, 0, GETVAL)), int(errno), int(EIDRM), int(EINVAL); +#endif + return false; + } + + self->clearError(); + return true; +} + + +QT_END_NAMESPACE + +#endif // QT_CONFIG(sysv_sem) |