qlockfile_unix.cpp

Absolute File Name:/home/qt/qt5_coco/qt5/qtbase/src/corelib/io/qlockfile_unix.cpp
Source codeSwitch to Preprocessed file
LineSourceCount
1/****************************************************************************-
2**-
3** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>-
4** Copyright (C) 2016 Intel Corporation.-
5** Copyright (C) 2016 The Qt Company Ltd.-
6** Contact: https://www.qt.io/licensing/-
7**-
8** This file is part of the QtCore module of the Qt Toolkit.-
9**-
10** $QT_BEGIN_LICENSE:LGPL$-
11** Commercial License Usage-
12** Licensees holding valid commercial Qt licenses may use this file in-
13** accordance with the commercial license agreement provided with the-
14** Software or, alternatively, in accordance with the terms contained in-
15** a written agreement between you and The Qt Company. For licensing terms-
16** and conditions see https://www.qt.io/terms-conditions. For further-
17** information use the contact form at https://www.qt.io/contact-us.-
18**-
19** GNU Lesser General Public License Usage-
20** Alternatively, this file may be used under the terms of the GNU Lesser-
21** General Public License version 3 as published by the Free Software-
22** Foundation and appearing in the file LICENSE.LGPL3 included in the-
23** packaging of this file. Please review the following information to-
24** ensure the GNU Lesser General Public License version 3 requirements-
25** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.-
26**-
27** GNU General Public License Usage-
28** Alternatively, this file may be used under the terms of the GNU-
29** General Public License version 2.0 or (at your option) the GNU General-
30** Public license version 3 or any later version approved by the KDE Free-
31** Qt Foundation. The licenses are as published by the Free Software-
32** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3-
33** included in the packaging of this file. Please review the following-
34** information to ensure the GNU General Public License requirements will-
35** be met: https://www.gnu.org/licenses/gpl-2.0.html and-
36** https://www.gnu.org/licenses/gpl-3.0.html.-
37**-
38** $QT_END_LICENSE$-
39**-
40****************************************************************************/-
41-
42#include "private/qlockfile_p.h"-
43-
44#include "QtCore/qtemporaryfile.h"-
45#include "QtCore/qcoreapplication.h"-
46#include "QtCore/qfileinfo.h"-
47#include "QtCore/qdebug.h"-
48#include "QtCore/qdatetime.h"-
49#include "QtCore/qfileinfo.h"-
50#include "QtCore/qcache.h"-
51#include "QtCore/qglobalstatic.h"-
52#include "QtCore/qmutex.h"-
53-
54#include "private/qcore_unix_p.h" // qt_safe_open-
55#include "private/qabstractfileengine_p.h"-
56#include "private/qtemporaryfile_p.h"-
57-
58#if !defined(Q_OS_INTEGRITY)-
59#include <sys/file.h> // flock-
60#endif-
61-
62#include <sys/types.h> // kill-
63#include <signal.h> // kill-
64#include <unistd.h> // gethostname-
65-
66#if defined(Q_OS_OSX)-
67# include <libproc.h>-
68#elif defined(Q_OS_LINUX)-
69# include <unistd.h>-
70# include <cstdio>-
71#elif defined(Q_OS_HAIKU)-
72# include <kernel/OS.h>-
73#elif defined(Q_OS_BSD4) && !defined(Q_OS_IOS)-
74# include <sys/cdefs.h>-
75# include <sys/param.h>-
76# include <sys/sysctl.h>-
77# if !defined(Q_OS_NETBSD)-
78# include <sys/user.h>-
79# endif-
80#endif-
81-
82QT_BEGIN_NAMESPACE-
83-
84static QByteArray localHostName() // from QHostInfo::localHostName(), modified to return a QByteArray-
85{-
86 QByteArray hostName(512, Qt::Uninitialized);-
87 if (gethostname(hostName.data(), hostName.size()) == -1)-
88 return QByteArray();-
89 hostName.truncate(strlen(hostName.data()));-
90 return hostName;-
91}-
92-
93// ### merge into qt_safe_write?-
94static qint64 qt_write_loop(int fd, const char *data, qint64 len)-
95{-
96 qint64 pos = 0;-
97 while (pos < len) {-
98 const qint64 ret = qt_safe_write(fd, data + pos, len - pos);-
99 if (ret == -1) // e.g. partition full-
100 return pos;-
101 pos += ret;-
102 }-
103 return pos;-
104}-
105-
106int QLockFilePrivate::checkFcntlWorksAfterFlock(const QString &fn)-
107{-
108#ifndef QT_NO_TEMPORARYFILE-
109 QTemporaryFile file(fn);-
110 if (!file.open())-
111 return 0;-
112 const int fd = file.d_func()->engine()->handle();-
113#if defined(LOCK_EX) && defined(LOCK_NB)-
114 if (flock(fd, LOCK_EX | LOCK_NB) == -1) // other threads, and other processes on a local fs-
115 return 0;-
116#endif-
117 struct flock flockData;-
118 flockData.l_type = F_WRLCK;-
119 flockData.l_whence = SEEK_SET;-
120 flockData.l_start = 0;-
121 flockData.l_len = 0; // 0 = entire file-
122 flockData.l_pid = getpid();-
123 if (fcntl(fd, F_SETLK, &flockData) == -1) // for networked filesystems-
124 return 0;-
125 return 1;-
126#else-
127 return 0;-
128#endif-
129}-
130-
131// Cache the result of checkFcntlWorksAfterFlock for each directory a lock-
132// file is created in because in some filesystems, like NFS, both locks-
133// are the same. This does not take into account a filesystem changing.-
134// QCache is set to hold a maximum of 10 entries, this is to avoid unbounded-
135// growth, this is caching directories of files and it is assumed a low number-
136// will be sufficient.-
137typedef QCache<QString, bool> CacheType;-
138Q_GLOBAL_STATIC_WITH_ARGS(CacheType, fcntlOK, (10));-
139static QBasicMutex fcntlLock;-
140-
141/*!-
142 \internal-
143 Checks that the OS isn't using POSIX locks to emulate flock().-
144 \macos is one of those.-
145*/-
146static bool fcntlWorksAfterFlock(const QString &fn)-
147{-
148 QMutexLocker lock(&fcntlLock);-
149 if (fcntlOK.isDestroyed())-
150 return QLockFilePrivate::checkFcntlWorksAfterFlock(fn);-
151 bool *worksPtr = fcntlOK->object(fn);-
152 if (worksPtr)-
153 return *worksPtr;-
154-
155 const bool val = QLockFilePrivate::checkFcntlWorksAfterFlock(fn);-
156 worksPtr = new bool(val);-
157 fcntlOK->insert(fn, worksPtr);-
158-
159 return val;-
160}-
161-
162static bool setNativeLocks(const QString &fileName, int fd)-
163{-
164#if defined(LOCK_EX) && defined(LOCK_NB)-
165 if (flock(fd, LOCK_EX | LOCK_NB) == -1) // other threads, and other processes on a local fs-
166 return false;-
167#endif-
168 struct flock flockData;-
169 flockData.l_type = F_WRLCK;-
170 flockData.l_whence = SEEK_SET;-
171 flockData.l_start = 0;-
172 flockData.l_len = 0; // 0 = entire file-
173 flockData.l_pid = getpid();-
174 if (fcntlWorksAfterFlock(QDir::cleanPath(QFileInfo(fileName).absolutePath()) + QString('/'))-
175 && fcntl(fd, F_SETLK, &flockData) == -1) { // for networked filesystems-
176 return false;-
177 }-
178 return true;-
179}-
180-
181QLockFile::LockError QLockFilePrivate::tryLock_sys()-
182{-
183 // Assemble data, to write in a single call to write-
184 // (otherwise we'd have to check every write call)-
185 // Use operator% from the fast builder to avoid multiple memory allocations.-
186 QByteArray fileData = QByteArray::number(QCoreApplication::applicationPid()) % '\n'-
187 % QCoreApplication::applicationName().toUtf8() % '\n'-
188 % localHostName() % '\n';-
189-
190 const QByteArray lockFileName = QFile::encodeName(fileName);-
191 const int fd = qt_safe_open(lockFileName.constData(), O_WRONLY | O_CREAT | O_EXCL, 0666);-
192 if (fd < 0) {-
193 switch (errno) {-
194 case EEXIST:-
195 return QLockFile::LockFailedError;-
196 case EACCES:-
197 case EROFS:-
198 return QLockFile::PermissionError;-
199 default:-
200 return QLockFile::UnknownError;-
201 }-
202 }-
203 // Ensure nobody else can delete the file while we have it-
204 if (!setNativeLocks(fileName, fd)) {-
205 const int errnoSaved = errno;-
206 qWarning() << "setNativeLocks failed:" << qt_error_string(errnoSaved);-
207 }-
208-
209 if (qt_write_loop(fd, fileData.constData(), fileData.size()) < fileData.size()) {-
210 close(fd);-
211 if (!QFile::remove(fileName))-
212 qWarning("QLockFile: Could not remove our own lock file %s.", qPrintable(fileName));-
213 return QLockFile::UnknownError; // partition full-
214 }-
215-
216 // We hold the lock, continue.-
217 fileHandle = fd;-
218-
219 // Sync to disk if possible. Ignore errors (e.g. not supported).-
220#if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0-
221 fdatasync(fileHandle);-
222#else-
223 fsync(fileHandle);-
224#endif-
225-
226 return QLockFile::NoError;-
227}-
228-
229bool QLockFilePrivate::removeStaleLock()-
230{-
231 const QByteArray lockFileName = QFile::encodeName(fileName);-
232 const int fd = qt_safe_open(lockFileName.constData(), O_WRONLY, 0666);-
233 if (fd < 0) // gone already?-
234 return false;-
235 bool success = setNativeLocks(fileName, fd) && (::unlink(lockFileName) == 0);-
236 close(fd);-
237 return success;-
238}-
239-
240bool QLockFilePrivate::isApparentlyStale() const-
241{-
242 qint64 pid;-
243 QString hostname, appname;-
244 if (getLockInfo(&pid, &hostname, &appname)) {-
245 if (hostname.isEmpty() || hostname == QString::fromLocal8Bit(localHostName())) {-
246 if (::kill(pid, 0) == -1 && errno == ESRCH)-
247 return true; // PID doesn't exist anymore-
248 const QString processName = processNameByPid(pid);-
249 if (!processName.isEmpty()) {-
250 QFileInfo fi(appname);-
251 if (fi.isSymLink())-
252 fi.setFile(fi.symLinkTarget());-
253 if (processName != fi.fileName())-
254 return true; // PID got reused by a different application.-
255 }-
256 }-
257 }-
258 const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime());-
259 return staleLockTime > 0 && age > staleLockTime;-
260}-
261-
262QString QLockFilePrivate::processNameByPid(qint64 pid)-
263{-
264#if defined(Q_OS_OSX)-
265 char name[1024];-
266 proc_name(pid, name, sizeof(name) / sizeof(char));-
267 return QFile::decodeName(name);-
268#elif defined(Q_OS_LINUX)-
269 if (!QFile::exists(QStringLiteral("/proc/version")))
!QFile::exists...al_temp; }()))Description
TRUEnever evaluated
FALSEevaluated 12 times by 1 test
Evaluated by:
  • tst_QLockFile
0-12
270 return QString();
never executed: return QString();
0
271 char exePath[64];-
272 char buf[PATH_MAX + 1];-
273 sprintf(exePath, "/proc/%lld/exe", pid);-
274 size_t len = (size_t)readlink(exePath, buf, sizeof(buf));-
275 if (len >= sizeof(buf)) {
len >= sizeof(buf)Description
TRUEnever evaluated
FALSEevaluated 12 times by 1 test
Evaluated by:
  • tst_QLockFile
0-12
276 // The pid is gone. Return some invalid process name to fail the test.-
277 return QStringLiteral("/ERROR/");
never executed: return ([]() -> QString { enum { Size = sizeof(u"" "/ERROR/")/2 - 1 }; static const QStaticStringData<Size> qstring_literal = { { { { -1 } }, Size, 0, 0, sizeof(QStringData) }, u"" "/ERROR/" }; QStringDataPtr holder = { qstring_literal.data_ptr() }; const QString qstring_literal_temp(holder); return qstring_literal_temp; }());
never executed: return qstring_literal_temp;
0
278 }-
279 buf[len] = 0;-
280 return QFileInfo(QFile::decodeName(buf)).fileName();
executed 12 times by 1 test: return QFileInfo(QFile::decodeName(buf)).fileName();
Executed by:
  • tst_QLockFile
12
281#elif defined(Q_OS_HAIKU)-
282 thread_info info;-
283 if (get_thread_info(pid, &info) != B_OK)-
284 return QString();-
285 return QFile::decodeName(info.name);-
286#elif defined(Q_OS_BSD4) && !defined(Q_OS_IOS)-
287# if defined(Q_OS_NETBSD)-
288 struct kinfo_proc2 kp;-
289 int mib[6] = { CTL_KERN, KERN_PROC2, KERN_PROC_PID, (int)pid, sizeof(struct kinfo_proc2), 1 };-
290# elif defined(Q_OS_OPENBSD)-
291 struct kinfo_proc kp;-
292 int mib[6] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)pid, sizeof(struct kinfo_proc), 1 };-
293# else-
294 struct kinfo_proc kp;-
295 int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)pid };-
296# endif-
297 size_t len = sizeof(kp);-
298 u_int mib_len = sizeof(mib)/sizeof(u_int);-
299-
300 if (sysctl(mib, mib_len, &kp, &len, NULL, 0) < 0)-
301 return QString();-
302-
303# if defined(Q_OS_OPENBSD) || defined(Q_OS_NETBSD)-
304 if (kp.p_pid != pid)-
305 return QString();-
306 QString name = QFile::decodeName(kp.p_comm);-
307# else-
308 if (kp.ki_pid != pid)-
309 return QString();-
310 QString name = QFile::decodeName(kp.ki_comm);-
311# endif-
312 return name;-
313-
314#else-
315 Q_UNUSED(pid);-
316 return QString();-
317#endif-
318}-
319-
320void QLockFile::unlock()-
321{-
322 Q_D(QLockFile);-
323 if (!d->isLocked)-
324 return;-
325 close(d->fileHandle);-
326 d->fileHandle = -1;-
327 if (!QFile::remove(d->fileName)) {-
328 qWarning() << "Could not remove our own lock file" << d->fileName << "maybe permissions changed meanwhile?";-
329 // This is bad because other users of this lock file will now have to wait for the stale-lock-timeout...-
330 }-
331 d->lockError = QLockFile::NoError;-
332 d->isLocked = false;-
333}-
334-
335QT_END_NAMESPACE-
Source codeSwitch to Preprocessed file

Generated by Squish Coco Non-Commercial 4.3.0-BETA-master-30-08-2018-4cb69e9