1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

Merge branch 'bitg_multiple_instances' into back_in_the_game

This commit is contained in:
tux3 2015-04-24 19:50:18 +02:00
commit dc5f812946
No known key found for this signature in database
GPG Key ID: 7E086DD661263264
7 changed files with 158 additions and 4 deletions

View File

@ -430,6 +430,7 @@ SOURCES += \
src/core/coreencryption.cpp \ src/core/coreencryption.cpp \
src/core/corefile.cpp \ src/core/corefile.cpp \
src/core/corestructs.cpp \ src/core/corestructs.cpp \
src/profilelocker.cpp
HEADERS += \ HEADERS += \
src/audio.h \ src/audio.h \
@ -454,3 +455,4 @@ HEADERS += \
src/widget/gui.h \ src/widget/gui.h \
src/toxme.h \ src/toxme.h \
src/misc/qrwidget.h \ src/misc/qrwidget.h \
src/profilelocker.h

View File

@ -22,6 +22,7 @@
#include "src/widget/gui.h" #include "src/widget/gui.h"
#include "src/historykeeper.h" #include "src/historykeeper.h"
#include "src/audio.h" #include "src/audio.h"
#include "src/profilelocker.h"
#include "corefile.h" #include "corefile.h"
#include <tox/tox.h> #include <tox/tox.h>
@ -884,6 +885,18 @@ QByteArray Core::loadToxSave(QString path)
QByteArray data; QByteArray data;
loadPath = ""; // if not empty upon return, then user forgot a password and is switching loadPath = ""; // if not empty upon return, then user forgot a password and is switching
// If we can't get a lock, then another instance is already using that profile
while (!ProfileLocker::lock(QFileInfo(path).baseName()))
{
qWarning() << "Profile "<<QFileInfo(path).baseName()<<" is already in use, pick another";
GUI::showWarning(tr("Profile already in use"),
tr("Your profile is already used by another qTox\n"
"Please select another profile"));
path = Settings::getInstance().askProfiles();
Settings::getInstance().switchProfile(QFileInfo(path).baseName());
HistoryKeeper::resetInstance();
}
QFile configurationFile(path); QFile configurationFile(path);
qDebug() << "Core::loadConfiguration: reading from " << path; qDebug() << "Core::loadConfiguration: reading from " << path;
@ -959,8 +972,19 @@ void Core::saveConfiguration()
saveConfiguration(path); saveConfiguration(path);
} }
void Core::switchConfiguration(const QString& profile) void Core::switchConfiguration(const QString& _profile)
{ {
QString profile = QFileInfo(_profile).baseName();
// If we can't get a lock, then another instance is already using that profile
while (!profile.isEmpty() && !ProfileLocker::lock(profile))
{
qWarning() << "Profile "<<profile<<" is already in use, pick another";
GUI::showWarning(tr("Profile already in use"),
tr("Your profile is already used by another qTox instance\n"
"Please select another profile"));
profile = QFileInfo(Settings::getInstance().askProfiles()).baseName();
}
if (profile.isEmpty()) if (profile.isEmpty())
qDebug() << "Core: creating new Id"; qDebug() << "Core: creating new Id";
else else

View File

@ -21,6 +21,7 @@
#include "src/widget/toxuri.h" #include "src/widget/toxuri.h"
#include "src/widget/toxsave.h" #include "src/widget/toxsave.h"
#include "src/autoupdate.h" #include "src/autoupdate.h"
#include "src/profilelocker.h"
#include <QApplication> #include <QApplication>
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QDateTime> #include <QDateTime>
@ -213,6 +214,13 @@ int main(int argc, char *argv[])
ipc.registerEventHandler("save", &toxSaveEventHandler); ipc.registerEventHandler("save", &toxSaveEventHandler);
ipc.registerEventHandler("activate", &toxActivateEventHandler); ipc.registerEventHandler("activate", &toxActivateEventHandler);
// If we're the IPC owner and we just started, then
// either we're the only running instance or any other instance
// is already so frozen it lost ownership.
// It's safe to remove any potential stale locks in this situation.
if (ipc.isCurrentOwner())
ProfileLocker::clearAllLocks();
if (parser.positionalArguments().size() > 0) if (parser.positionalArguments().size() > 0)
{ {
QString firstParam(parser.positionalArguments()[0]); QString firstParam(parser.positionalArguments()[0]);
@ -254,7 +262,7 @@ int main(int argc, char *argv[])
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
else if (!ipc.isCurrentOwner()) else if (!ipc.isCurrentOwner() && !parser.isSet("p"))
{ {
uint32_t dest = 0; uint32_t dest = 0;
if (parser.isSet("p")) if (parser.isSet("p"))

View File

@ -215,8 +215,11 @@ void Settings::load()
setProxyType(s.value("proxyType", static_cast<int>(ProxyType::ptNone)).toInt()); setProxyType(s.value("proxyType", static_cast<int>(ProxyType::ptNone)).toInt());
proxyAddr = s.value("proxyAddr", "").toString(); proxyAddr = s.value("proxyAddr", "").toString();
proxyPort = s.value("proxyPort", 0).toInt(); proxyPort = s.value("proxyPort", 0).toInt();
if (currentProfile.isEmpty())
{
currentProfile = s.value("currentProfile", "").toString(); currentProfile = s.value("currentProfile", "").toString();
currentProfileId = makeProfileId(currentProfile); currentProfileId = makeProfileId(currentProfile);
}
autoAwayTime = s.value("autoAwayTime", 10).toInt(); autoAwayTime = s.value("autoAwayTime", 10).toInt();
checkUpdates = s.value("checkUpdates", false).toBool(); checkUpdates = s.value("checkUpdates", false).toBool();
showWindow = s.value("showWindow", true).toBool(); showWindow = s.value("showWindow", true).toBool();

69
src/profilelocker.cpp Normal file
View File

@ -0,0 +1,69 @@
#include "profilelocker.h"
#include "src/misc/settings.h"
#include <QDir>
#include <QDebug>
using namespace std;
unique_ptr<QLockFile> ProfileLocker::lockfile;
QString ProfileLocker::curLockName;
QString ProfileLocker::lockPathFromName(const QString& name)
{
return Settings::getInstance().getSettingsDirPath()+'/'+name+".lock";
}
bool ProfileLocker::isLockable(QString profile)
{
// If we already have the lock, it's definitely lockable
if (lockfile && curLockName == profile)
return true;
QLockFile newLock(lockPathFromName(profile));
return newLock.tryLock();
}
bool ProfileLocker::lock(QString profile)
{
if (lockfile && curLockName == profile)
return true;
QLockFile* newLock = new QLockFile(lockPathFromName(profile));
if (!newLock->tryLock())
{
delete newLock;
return false;
}
unlock();
lockfile.reset(newLock);
curLockName = profile;
return true;
}
void ProfileLocker::unlock()
{
if (!lockfile)
return;
lockfile->unlock();
delete lockfile.release();
lockfile = nullptr;
curLockName.clear();
}
void ProfileLocker::clearAllLocks()
{
qDebug() << "ProfileLocker::clearAllLocks: Wiping out all lock files";
if (lockfile)
unlock();
QDir dir(Settings::getInstance().getSettingsDirPath());
dir.setFilter(QDir::Files);
dir.setNameFilters({"*.lock"});
QFileInfoList files = dir.entryInfoList();
for (QFileInfo fileInfo : files)
{
QFile file(fileInfo.absoluteFilePath());
file.remove();
}
}

40
src/profilelocker.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef PROFILELOCKER_H
#define PROFILELOCKER_H
#include <QLockFile>
#include <memory>
/// Locks a Tox profile so that multiple instances can not use the same profile.
/// Only one lock can be acquired at the same time, which means
/// that there is little need for manually unlocking.
/// The current lock will expire if you exit or acquire a new one.
class ProfileLocker
{
private:
ProfileLocker()=delete;
public:
/// Checks if a profile is currently locked by *another* instance
/// If we own the lock, we consider it lockable
/// There is no guarantee that the result will still be valid by the
/// time it is returned, this is provided on a best effort basis
static bool isLockable(QString profile);
/// Tries to acquire the lock on a profile, will not block
/// Returns true if we already own the lock
static bool lock(QString profile);
/// Releases the lock on the current profile
static void unlock();
/// Releases all locks on all profiles
/// DO NOT call unless all we're the only qTox instance
/// and we don't hold any lock yet.
static void clearAllLocks();
private:
static QString lockPathFromName(const QString& name);
private:
static std::unique_ptr<QLockFile> lockfile;
static QString curLockName;
};
#endif // PROFILELOCKER_H

View File

@ -27,6 +27,7 @@
#include "src/widget/gui.h" #include "src/widget/gui.h"
#include "src/historykeeper.h" #include "src/historykeeper.h"
#include "src/misc/style.h" #include "src/misc/style.h"
#include "src/profilelocker.h"
#include <QLabel> #include <QLabel>
#include <QLineEdit> #include <QLineEdit>
#include <QGroupBox> #include <QGroupBox>
@ -247,6 +248,13 @@ void ProfileForm::onRenameClicked()
if (!QFile::exists(file) || GUI::askQuestion(tr("Profile already exists", "rename confirm title"), if (!QFile::exists(file) || GUI::askQuestion(tr("Profile already exists", "rename confirm title"),
tr("A profile named \"%1\" already exists. Do you want to erase it?", "rename confirm text").arg(cur))) tr("A profile named \"%1\" already exists. Do you want to erase it?", "rename confirm text").arg(cur)))
{ {
if (!ProfileLocker::lock(name))
{
GUI::showWarning(tr("Profile already exists", "rename failed title"),
tr("A profile named \"%1\" already exists and is in use.").arg(cur));
break;
}
QFile::rename(dir.filePath(cur+Core::TOX_EXT), file); QFile::rename(dir.filePath(cur+Core::TOX_EXT), file);
QFile::rename(dir.filePath(cur+".ini"), dir.filePath(name+".ini")); QFile::rename(dir.filePath(cur+".ini"), dir.filePath(name+".ini"));
bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name); bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name);