Pull QLockFile updates from the qtbase repository.

This commit is contained in:
Felix Geyer 2015-07-21 23:11:02 +02:00
parent 2c17fdcff0
commit ceeb72a277
3 changed files with 116 additions and 31 deletions

View File

@ -83,10 +83,7 @@ public:
// Returns \c true if the lock belongs to dead PID, or is old.
// The attempt to delete it will tell us if it was really stale or not, though.
bool isApparentlyStale() const;
#ifdef Q_OS_UNIX
static int checkFcntlWorksAfterFlock();
#endif
static QString processNameByPid(qint64 pid);
QString fileName;
#ifdef Q_OS_WIN

View File

@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -42,10 +43,17 @@
#include <sys/file.h> // flock
#include <sys/types.h> // kill
#include <signal.h> // kill
#include <unistd.h>
#include <unistd.h> // gethostname
#include <errno.h>
#if defined(Q_OS_MAC)
# include <libproc.h>
#elif defined(Q_OS_LINUX)
# include <unistd.h>
# include <cstdio>
#endif
QT_BEGIN_NAMESPACE
#define EINTR_LOOP(var, cmd) \
@ -77,13 +85,13 @@ static inline qint64 qt_safe_write(int fd, const void *data, qint64 len)
return ret;
}
static QString localHostName() // from QHostInfo::localHostName()
static QByteArray localHostName() // from QHostInfo::localHostName(), modified to return a QByteArray
{
char hostName[512];
if (gethostname(hostName, sizeof(hostName)) == -1)
return QString();
hostName[sizeof(hostName) - 1] = '\0';
return QString::fromLocal8Bit(hostName);
QByteArray hostName(512, Qt::Uninitialized);
if (gethostname(hostName.data(), hostName.size()) == -1)
return QByteArray();
hostName.truncate(strlen(hostName.data()));
return hostName;
}
// ### merge into qt_safe_write?
@ -122,8 +130,8 @@ QLockFile::LockError QLockFilePrivate::tryLock_sys()
// (otherwise we'd have to check every write call)
// Use operator% from the fast builder to avoid multiple memory allocations.
QByteArray fileData = QByteArray::number(QCoreApplication::applicationPid()) + '\n'
+ qAppName().toUtf8() + '\n'
+ localHostName().toUtf8() + '\n';
+ QCoreApplication::applicationName().toUtf8() + '\n'
+ localHostName() + '\n';
const QByteArray lockFileName = QFile::encodeName(fileName);
const int fd = qt_safe_open(lockFileName.constData(), O_WRONLY | O_CREAT | O_EXCL, 0644);
@ -170,16 +178,48 @@ bool QLockFilePrivate::isApparentlyStale() const
{
qint64 pid;
QString hostname, appname;
if (!getLockInfo(&pid, &hostname, &appname))
return false;
if (hostname.isEmpty() || hostname == localHostName()) {
if (::kill(pid, 0) == -1 && errno == ESRCH)
return true; // PID doesn't exist anymore
if (getLockInfo(&pid, &hostname, &appname)) {
if (hostname.isEmpty() || hostname == QString::fromLocal8Bit(localHostName())) {
if (::kill(pid, 0) == -1 && errno == ESRCH)
return true; // PID doesn't exist anymore
const QString processName = processNameByPid(pid);
if (!processName.isEmpty()) {
QFileInfo fi(appname);
if (fi.isSymLink())
fi.setFile(fi.symLinkTarget());
if (processName.toLower() != fi.fileName().toLower())
return true; // PID got reused by a different application.
}
}
}
const qint64 age = QFileInfo(fileName).lastModified().secsTo(QDateTime::currentDateTime()) * 1000;
return staleLockTime > 0 && age > staleLockTime;
}
QString QLockFilePrivate::processNameByPid(qint64 pid)
{
#if defined(Q_OS_MAC)
char name[1024];
proc_name(pid, name, sizeof(name) / sizeof(char));
return QFile::decodeName(name);
#elif defined(Q_OS_LINUX)
if (!QFile::exists(QString("/proc/version")))
return QString();
char exePath[64];
char buf[PATH_MAX + 1];
sprintf(exePath, "/proc/%lld/exe", pid);
size_t len = static_cast<size_t>(readlink(exePath, buf, sizeof(buf)));
if (len >= sizeof(buf)) {
// The pid is gone. Return some invalid process name to fail the test.
return QString("/ERROR/");
}
buf[len] = 0;
return QFileInfo(QFile::decodeName(buf)).fileName();
#else
return QString();
#endif
}
void QLockFile::unlock()
{
Q_D(QLockFile);
@ -191,7 +231,6 @@ void QLockFile::unlock()
qWarning() << "Could not remove our own lock file" << d->fileName << "maybe permissions changed meanwhile?";
// This is bad because other users of this lock file will now have to wait for the stale-lock-timeout...
}
QFile::remove(d->fileName);
d->lockError = QLockFile::NoError;
d->isLocked = false;
}

View File

@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -134,27 +135,75 @@ bool QLockFilePrivate::isApparentlyStale() const
{
qint64 pid;
QString hostname, appname;
if (!getLockInfo(&pid, &hostname, &appname))
return false;
// On WinRT there seems to be no way of obtaining information about other
// processes due to sandboxing
#ifndef Q_OS_WINRT
if (hostname == QString::fromLocal8Bit(localHostName())) {
HANDLE procHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!procHandle)
return true;
// We got a handle but check if process is still alive
DWORD dwR = ::WaitForSingleObject(procHandle, 0);
::CloseHandle(procHandle);
if (dwR == WAIT_TIMEOUT)
return true;
if (getLockInfo(&pid, &hostname, &appname)) {
if (hostname.isEmpty() || hostname == QString::fromLocal8Bit(localHostName())) {
HANDLE procHandle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!procHandle)
return true;
// We got a handle but check if process is still alive
DWORD dwR = ::WaitForSingleObject(procHandle, 0);
::CloseHandle(procHandle);
if (dwR == WAIT_TIMEOUT)
return true;
const QString processName = processNameByPid(pid);
if (!processName.isEmpty() && processName != appname)
return true; // PID got reused by a different application.
}
}
#endif // !Q_OS_WINRT
#else // !Q_OS_WINRT
Q_UNUSED(pid);
Q_UNUSED(hostname);
Q_UNUSED(appname);
#endif // Q_OS_WINRT
const qint64 age = QFileInfo(fileName).lastModified().msecsTo(QDateTime::currentDateTime());
return staleLockTime > 0 && age > staleLockTime;
}
QString QLockFilePrivate::processNameByPid(qint64 pid)
{
#if !defined(Q_OS_WINRT) && !defined(Q_OS_WINCE)
typedef DWORD (WINAPI *GetModuleFileNameExFunc)(HANDLE, HMODULE, LPTSTR, DWORD);
HMODULE hPsapi = LoadLibraryA("psapi");
if (!hPsapi)
return QString();
GetModuleFileNameExFunc qGetModuleFileNameEx
= (GetModuleFileNameExFunc)GetProcAddress(hPsapi, "GetModuleFileNameExW");
if (!qGetModuleFileNameEx) {
FreeLibrary(hPsapi);
return QString();
}
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, DWORD(pid));
if (!hProcess) {
FreeLibrary(hPsapi);
return QString();
}
wchar_t buf[MAX_PATH];
const DWORD length = qGetModuleFileNameEx(hProcess, NULL, buf, sizeof(buf) / sizeof(wchar_t));
CloseHandle(hProcess);
FreeLibrary(hPsapi);
if (!length)
return QString();
QString name = QString::fromWCharArray(buf, length);
int i = name.lastIndexOf(QLatin1Char('\\'));
if (i >= 0)
name.remove(0, i + 1);
i = name.lastIndexOf(QLatin1Char('.'));
if (i >= 0)
name.truncate(i);
return name;
#else
Q_UNUSED(pid);
return QString();
#endif
}
void QLockFile::unlock()
{
Q_D(QLockFile);