1 /*******************************************************************
2
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2014 Fachhochschule Potsdam - http://fh-potsdam.de
5
6 Fritzing is free 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 Fritzing 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 Fritzing. If not, see <http://www.gnu.org/licenses/>.
18
19 ********************************************************************
20
21 $Revision: 6904 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $
24
25 ********************************************************************/
26
27 #include "lockmanager.h"
28 #include "folderutils.h"
29 #include "textutils.h"
30
31 #include <QTimer>
32 #include <QPointer>
33 #include <QMutex>
34 #include <QMutexLocker>
35 #include <QDateTime>
36
37 const QString LockManager::LockedFileName = "___lockfile___.txt";
38 const long LockManager::FastTime = 2000;
39 const long LockManager::SlowTime = 240000;
40
41 static LockManager TheLockManager;
42 static QHash<long, QPointer<QTimer> > TheTimers;
43 static QMultiHash<long, LockedFile *> TheLockedFiles;
44 static QMutex TheMutex;
45
LockedFile(const QString & filename,long freq)46 LockedFile::LockedFile(const QString & filename, long freq) {
47 file.setFileName(filename);
48 frequency = freq;
49 }
50
touch()51 bool LockedFile::touch() {
52 if (file.open(QFile::WriteOnly)) {
53 file.write("a");
54 file.close();
55 return true;
56 }
57
58 return false;
59 }
60
61 /////////////////////////////////////////////////
62
LockManager()63 LockManager::LockManager() : QObject()
64 {
65 }
66
~LockManager()67 LockManager::~LockManager()
68 {
69 foreach (QTimer * timer, TheTimers) {
70 if (timer) timer->stop();
71 }
72 TheTimers.clear();
73 }
74
cleanup()75 void LockManager::cleanup() {
76 foreach (QTimer * timer, TheTimers) {
77 if (timer) {
78 timer->stop();
79 delete timer;
80 }
81 }
82 TheTimers.clear();
83 }
84
touchFiles()85 void LockManager::touchFiles() {
86 QTimer * timer = qobject_cast<QTimer *>(sender());
87 if (timer == NULL) return;
88
89 QMutexLocker locker(&TheMutex);
90 foreach (LockedFile * lockedFile, TheLockedFiles.values(timer->interval())) {
91 lockedFile->touch();
92 }
93 }
94
initLockedFiles(const QString & prefix,QString & folder,QHash<QString,LockedFile * > & lockedFiles,long touchFrequency)95 void LockManager::initLockedFiles(const QString & prefix, QString & folder, QHash<QString, LockedFile *> & lockedFiles, long touchFrequency) {
96 // first create our own unique folder and lock it
97 QDir backupDir(FolderUtils::getUserDataStorePath(prefix));
98 QString lockedSubfolder = TextUtils::getRandText();
99 backupDir.mkdir(lockedSubfolder);
100 folder = backupDir.absoluteFilePath(lockedSubfolder);
101 if (touchFrequency > 0) {
102 LockedFile * lockedFile = makeLockedFile(folder + "/" + LockedFileName, touchFrequency);
103 lockedFiles.insert(lockedSubfolder, lockedFile);
104 }
105 }
106
makeLockedFile(const QString & path,long touchFrequency)107 LockedFile * LockManager::makeLockedFile(const QString & path, long touchFrequency) {
108 LockedFile * lockedFile = new LockedFile(path, touchFrequency);
109 lockedFile->touch();
110 TheMutex.lock();
111 TheLockedFiles.insert(touchFrequency, lockedFile);
112 TheMutex.unlock();
113 QTimer * timer = TheTimers.value(touchFrequency, NULL);
114 if (timer == NULL) {
115 timer = new QTimer();
116 timer->setInterval(touchFrequency);
117 timer->setSingleShot(false);
118 QObject::connect(timer, SIGNAL(timeout()), &TheLockManager, SLOT(touchFiles()));
119 timer->start();
120 TheTimers.insert(touchFrequency, timer);
121 }
122 return lockedFile;
123 }
124
125
releaseLockedFiles(const QString & folder,QHash<QString,LockedFile * > & lockedFiles)126 void LockManager::releaseLockedFiles(const QString & folder, QHash<QString, LockedFile *> & lockedFiles)
127 {
128 // remove backup files; this is a clean exit
129 releaseLockedFiles(folder, lockedFiles, true);
130 }
131
releaseLockedFiles(const QString & folder,QHash<QString,LockedFile * > & lockedFiles,bool remove)132 void LockManager::releaseLockedFiles(const QString & folder, QHash<QString, LockedFile *> & lockedFiles, bool remove)
133 {
134 QDir backupDir(folder);
135 backupDir.cdUp();
136 foreach (QString sub, lockedFiles.keys()) {
137 LockedFile * lockedFile = lockedFiles.value(sub);
138 TheMutex.lock();
139 TheLockedFiles.remove(lockedFile->frequency, lockedFile);
140 TheMutex.unlock();
141 if (remove) {
142 FolderUtils::rmdir(backupDir.absoluteFilePath(sub));
143 }
144 delete lockedFile;
145 }
146 lockedFiles.clear();
147 }
148
checkLockedFiles(const QString & prefix,QFileInfoList & backupList,QHash<QString,LockedFile * > & lockedFiles,bool recurse,long touchFrequency)149 void LockManager::checkLockedFiles(const QString & prefix, QFileInfoList & backupList, QHash<QString, LockedFile *> & lockedFiles, bool recurse, long touchFrequency)
150 {
151 QDir backupDir(FolderUtils::getUserDataStorePath(prefix));
152 QFileInfoList dirList = backupDir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::NoSymLinks);
153 foreach (QFileInfo dirInfo, dirList) {
154 QDir dir(dirInfo.filePath());
155 QStringList filters;
156 //DebugDialog::debug(QString("looking in backup dir %1").arg(dir.absolutePath()));
157 QFileInfoList fileInfoList = dir.entryInfoList(filters, QDir::Files | QDir::Hidden | QDir::NoSymLinks);
158 bool gotRecurse = false;
159 if (recurse && fileInfoList.isEmpty()) {
160 gotRecurse = checkLockedFilesAux(dir, filters);
161 }
162
163 if (fileInfoList.isEmpty() && !gotRecurse) {
164 // could mean this backup folder is just being created by another process
165 // could also mean it's leftover crap.
166 // check the date and only delete if it's old
167
168 QDateTime lastModified = dirInfo.lastModified();
169 if (lastModified < QDateTime::currentDateTime().addMSecs(-2000 - touchFrequency)) {
170 FolderUtils::rmdir(dirInfo.filePath());
171 }
172
173 continue;
174 }
175
176 QFileInfo lockInfo(dir.absoluteFilePath(LockedFileName));
177 if (lockInfo.exists()) {
178 QDateTime lastModified = lockInfo.lastModified();
179 if (lastModified >= QDateTime::currentDateTime().addMSecs(-2000 - touchFrequency)) {
180 // somebody else owns the file
181 continue;
182 }
183 }
184
185 // we own the file
186 QString folder;
187 LockedFile * lockedFile = makeLockedFile(dir.absoluteFilePath(LockedFileName), touchFrequency);
188 lockedFiles.insert(dirInfo.fileName(), lockedFile);
189 foreach (QFileInfo fileInfo, fileInfoList) {
190 backupList << fileInfo;
191 }
192 }
193 }
194
checkLockedFilesAux(const QDir & parent,QStringList & filters)195 bool LockManager::checkLockedFilesAux(const QDir & parent, QStringList & filters)
196 {
197 QFileInfoList dirList = parent.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::NoSymLinks);
198 foreach (QFileInfo dirInfo, dirList) {
199 QDir dir(dirInfo.filePath());
200 //DebugDialog::debug(QString("looking in backup dir %1").arg(dir.absolutePath()));
201 QFileInfoList fileInfoList = dir.entryInfoList(filters, QDir::Files | QDir::Hidden | QDir::NoSymLinks);
202 if (!fileInfoList.isEmpty()) return true;
203
204 if (checkLockedFilesAux(dir, filters)) return true;
205 }
206
207 return false;
208 }