1 /*
2 Copyright © 2015-2019 by The qTox Project Contributors
3
4 This file is part of qTox, a Qt-based graphical interface for Tox.
5
6 qTox is libre software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 qTox is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with qTox. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20
21 #include "profilelocker.h"
22 #include "src/persistence/settings.h"
23 #include <QDebug>
24 #include <QDir>
25
26 /**
27 * @class ProfileLocker
28 * @brief Locks a Tox profile so that multiple instances can not use the same profile.
29 * Only one lock can be acquired at the same time, which means
30 * that there is little need for manually unlocking.
31 * The current lock will expire if you exit or acquire a new one.
32 */
33
34 using namespace std;
35
36 unique_ptr<QLockFile> ProfileLocker::lockfile;
37 QString ProfileLocker::curLockName;
38
lockPathFromName(const QString & name)39 QString ProfileLocker::lockPathFromName(const QString& name)
40 {
41 return Settings::getInstance().getSettingsDirPath() + '/' + name + ".lock";
42 }
43
44 /**
45 * @brief Checks if a profile is currently locked by *another* instance.
46 * If we own the lock, we consider it lockable.
47 * There is no guarantee that the result will still be valid by the
48 * time it is returned, this is provided on a best effort basis.
49 * @param profile Profile name to check.
50 * @return True, if profile locked, false otherwise.
51 */
isLockable(QString profile)52 bool ProfileLocker::isLockable(QString profile)
53 {
54 // If we already have the lock, it's definitely lockable
55 if (lockfile && curLockName == profile)
56 return true;
57
58 QLockFile newLock(lockPathFromName(profile));
59 return newLock.tryLock();
60 }
61
62 /**
63 * @brief Tries to acquire the lock on a profile, will not block.
64 * @param profile Profile to lock.
65 * @return Returns true if we already own the lock.
66 */
lock(QString profile)67 bool ProfileLocker::lock(QString profile)
68 {
69 if (lockfile && curLockName == profile)
70 return true;
71
72 QLockFile* newLock = new QLockFile(lockPathFromName(profile));
73 newLock->setStaleLockTime(0);
74 if (!newLock->tryLock()) {
75 delete newLock;
76 return false;
77 }
78
79 unlock();
80 lockfile.reset(newLock);
81 curLockName = profile;
82 return true;
83 }
84
85 /**
86 * @brief Releases the lock on the current profile.
87 */
unlock()88 void ProfileLocker::unlock()
89 {
90 if (!lockfile)
91 return;
92
93 lockfile->unlock();
94 lockfile.reset();
95 curLockName.clear();
96 }
97
98 /**
99 * @brief Check that we actually own the lock.
100 * In case the file was deleted on disk, restore it.
101 * If we can't get a lock, exit qTox immediately.
102 * If we never had a lock in the first place, exit immediately.
103 */
assertLock()104 void ProfileLocker::assertLock()
105 {
106 if (!lockfile) {
107 qCritical() << "assertLock: We don't seem to own any lock!";
108 deathByBrokenLock();
109 }
110
111 if (!QFile(lockPathFromName(curLockName)).exists()) {
112 QString tmp = curLockName;
113 unlock();
114 if (lock(tmp)) {
115 qCritical() << "assertLock: Lock file was lost, but could be restored";
116 } else {
117 qCritical() << "assertLock: Lock file was lost, and could *NOT* be restored";
118 deathByBrokenLock();
119 }
120 }
121 }
122
123 /**
124 * @brief Print an error then exit immediately.
125 */
deathByBrokenLock()126 void ProfileLocker::deathByBrokenLock()
127 {
128 qCritical() << "Lock is *BROKEN*, exiting immediately";
129 abort();
130 }
131
132 /**
133 * @brief Chacks, that profile locked.
134 * @return Returns true if we're currently holding a lock.
135 */
hasLock()136 bool ProfileLocker::hasLock()
137 {
138 return lockfile.operator bool();
139 }
140
141 /**
142 * @brief Get current locked profile name.
143 * @return Return the name of the currently loaded profile, a null string if there is none.
144 */
getCurLockName()145 QString ProfileLocker::getCurLockName()
146 {
147 if (lockfile)
148 return curLockName;
149 else
150 return QString();
151 }
152