blob: 79363e8c0f734ec0fb8148094236291710062c71 [file] [log] [blame]
Jared Lindblom2eead682013-01-12 20:26:21 -08001/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
2/*
3 * Copyright (c) 2012-2013 University of California, Los Angeles
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation;
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Author: Jared Lindblom <lindblom@cs.ucla.edu>
19 */
jareda9541812013-01-09 00:08:46 -080020
Jared Lindblom2eead682013-01-12 20:26:21 -080021#include "filesystemwatcher.h"
22
23FileSystemWatcher::FileSystemWatcher(QString dirPath, QObject* parent) :
24 QObject(parent),
jareda9541812013-01-09 00:08:46 -080025 m_watcher(new QFileSystemWatcher()),
Jared Lindblom2eead682013-01-12 20:26:21 -080026 m_timer(new QTimer()),
jareda9541812013-01-09 00:08:46 -080027 m_dirPath(dirPath)
28{
jareda9541812013-01-09 00:08:46 -080029 // add main directory to monitor
30 m_watcher->addPath(m_dirPath);
31
jareda9541812013-01-09 00:08:46 -080032 // register signals (callback functions)
Jared Lindblomf8d32ad2013-01-12 11:32:27 -080033 connect(m_watcher, SIGNAL(directoryChanged(QString)), this, SLOT(watcherCallbackSlot(QString)));
34 connect(m_timer, SIGNAL(timeout()), this, SLOT(timerCallbackSlot()));
jareda9541812013-01-09 00:08:46 -080035
Jared Lindbloma9996302013-01-12 00:25:16 -080036 // bootstrap
Jared Lindblom2eead682013-01-12 20:26:21 -080037 QTimer::singleShot(1000, this, SLOT(bootstrap()));
Jared Lindbloma9996302013-01-12 00:25:16 -080038
39 // start timer
Jared Lindblom8278b982013-01-12 15:45:09 -080040 m_timer->start(300000);
jareda9541812013-01-09 00:08:46 -080041}
42
Jared Lindblom4d1d00a2013-01-11 01:14:23 -080043FileSystemWatcher::~FileSystemWatcher()
jareda9541812013-01-09 00:08:46 -080044{
45 // clean up
jareda9541812013-01-09 00:08:46 -080046 delete m_watcher;
Jared Lindbloma9996302013-01-12 00:25:16 -080047 delete m_timer;
jareda9541812013-01-09 00:08:46 -080048}
49
Jared Lindblom2eead682013-01-12 20:26:21 -080050void FileSystemWatcher::bootstrap()
51{
52 // bootstrap specific steps
53 timerCallbackSlot();
54}
55
Jared Lindblomf8d32ad2013-01-12 11:32:27 -080056void FileSystemWatcher::watcherCallbackSlot(QString dirPath)
57{
58 // watcher specific steps
Jared Lindblomc1259722013-01-12 16:47:32 -080059#if DEBUG
Jared Lindblomf8d32ad2013-01-12 11:32:27 -080060 qDebug() << endl << "[WATCHER] Triggered Path: " << dirPath;
Jared Lindblomc1259722013-01-12 16:47:32 -080061#endif
Jared Lindblomf8d32ad2013-01-12 11:32:27 -080062 handleCallback(dirPath);
63}
64
65void FileSystemWatcher::timerCallbackSlot()
66{
67 // timer specific steps
Jared Lindblomc1259722013-01-12 16:47:32 -080068#if DEBUG
Jared Lindblomf8d32ad2013-01-12 11:32:27 -080069 qDebug() << endl << "[TIMER] Triggered Path: " << m_dirPath;
Jared Lindblomc1259722013-01-12 16:47:32 -080070#endif
Jared Lindblomf8d32ad2013-01-12 11:32:27 -080071 handleCallback(m_dirPath);
72}
73
74void FileSystemWatcher::handleCallback(QString dirPath)
jareda9541812013-01-09 00:08:46 -080075{
Jared Lindblom4d1d00a2013-01-11 01:14:23 -080076 // scan directory and populate file list
Jared Lindblom8278b982013-01-12 15:45:09 -080077 QHash<QString, qint64> currentState = scanDirectory(dirPath);
Jared Lindblom4d1d00a2013-01-11 01:14:23 -080078
Jared Lindblom264310d2013-01-12 01:18:58 -080079 // reconcile directory and report changes
Jared Lindblomc1259722013-01-12 16:47:32 -080080 std::vector<sEventInfo> dirChanges = reconcileDirectory(currentState, dirPath);
Jared Lindblomc1259722013-01-12 16:47:32 -080081#if DEBUG
82 // DEBUG: Print Changes
83 printChanges(dirChanges);
84#endif
Jared Lindblom2eead682013-01-12 20:26:21 -080085 // emit the signal
86 emit dirEventSignal(dirChanges);
jareda9541812013-01-09 00:08:46 -080087}
88
Jared Lindblom8278b982013-01-12 15:45:09 -080089QHash<QString, qint64> FileSystemWatcher::scanDirectory(QString dirPath)
Jared Lindbloma9996302013-01-12 00:25:16 -080090{
jareda9541812013-01-09 00:08:46 -080091 // list of files in directory
Jared Lindblom8278b982013-01-12 15:45:09 -080092 QHash<QString, qint64> currentState;
jareda9541812013-01-09 00:08:46 -080093
94 // directory iterator (recursive)
Jared Lindblom4d1d00a2013-01-11 01:14:23 -080095 QDirIterator dirIterator(dirPath, QDirIterator::Subdirectories |
jareda9541812013-01-09 00:08:46 -080096 QDirIterator::FollowSymlinks);
97
98 // iterate through directory recursively
99 while(dirIterator.hasNext())
100 {
101 // Get Next File/Dir
102 dirIterator.next();
103
104 // Get FileInfo
105 QFileInfo fileInfo = dirIterator.fileInfo();
106
107 // if not this directory or previous directory
108 if(fileInfo.absoluteFilePath() != ".." && fileInfo.absoluteFilePath() != ".")
109 {
Jared Lindbloma9996302013-01-12 00:25:16 -0800110 QString absFilePath = fileInfo.absoluteFilePath();
111
jareda9541812013-01-09 00:08:46 -0800112 // if this is a directory
113 if(fileInfo.isDir())
114 {
115 QStringList dirList = m_watcher->directories();
116
117 // if the directory is not already being watched
Jared Lindbloma9996302013-01-12 00:25:16 -0800118 if (absFilePath.startsWith(m_dirPath) && !dirList.contains(absFilePath))
jareda9541812013-01-09 00:08:46 -0800119 {
120 // add this directory to the watch list
Jared Lindbloma9996302013-01-12 00:25:16 -0800121 m_watcher->addPath(absFilePath);
jareda9541812013-01-09 00:08:46 -0800122 }
123 }
124 else
125 {
126 // add this file to the file list
Jared Lindblom8278b982013-01-12 15:45:09 -0800127 currentState.insert(absFilePath, fileInfo.created().toMSecsSinceEpoch());
jareda9541812013-01-09 00:08:46 -0800128 }
129 }
130 }
131
Jared Lindbloma9996302013-01-12 00:25:16 -0800132 return currentState;
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800133}
134
Jared Lindblomc1259722013-01-12 16:47:32 -0800135std::vector<sEventInfo> FileSystemWatcher::reconcileDirectory(QHash<QString, qint64> currentState, QString dirPath)
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800136{
Jared Lindbloma9996302013-01-12 00:25:16 -0800137 // list of files changed
Jared Lindblomc1259722013-01-12 16:47:32 -0800138 std::vector<sEventInfo> dirChanges;
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800139
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800140 // compare result (database/stored snapshot) to fileList (current snapshot)
Jared Lindblom8278b982013-01-12 15:45:09 -0800141 QMutableHashIterator<QString, qint64> i(m_storedState);
Jared Lindblom264310d2013-01-12 01:18:58 -0800142
Jared Lindbloma9996302013-01-12 00:25:16 -0800143 while(i.hasNext())
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800144 {
Jared Lindbloma9996302013-01-12 00:25:16 -0800145 i.next();
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800146
Jared Lindbloma9996302013-01-12 00:25:16 -0800147 QString absFilePath = i.key();
Jared Lindblom8278b982013-01-12 15:45:09 -0800148 qint64 storedCreated = i.value();
Jared Lindbloma9996302013-01-12 00:25:16 -0800149
Jared Lindblom8278b982013-01-12 15:45:09 -0800150 // if this file is in a level higher than
151 // this directory, ignore
Jared Lindblomf8d32ad2013-01-12 11:32:27 -0800152 if(!absFilePath.startsWith(dirPath))
153 {
154 continue;
155 }
156
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800157 // check file existence
Jared Lindbloma9996302013-01-12 00:25:16 -0800158 if(currentState.contains(absFilePath))
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800159 {
Jared Lindblom8278b982013-01-12 15:45:09 -0800160 qint64 currentCreated = currentState.value(absFilePath);
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800161
Jared Lindblom8278b982013-01-12 15:45:09 -0800162 if(storedCreated != currentCreated)
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800163 {
Jared Lindbloma9996302013-01-12 00:25:16 -0800164 // update stored state
Jared Lindblom8278b982013-01-12 15:45:09 -0800165 i.setValue(currentCreated);
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800166
167 // this file has been modified
Jared Lindblom264310d2013-01-12 01:18:58 -0800168 sEventInfo eventInfo;
169 eventInfo.event = MODIFIED;
Jared Lindblom8278b982013-01-12 15:45:09 -0800170 eventInfo.absFilePath = absFilePath.toStdString();
Jared Lindblom264310d2013-01-12 01:18:58 -0800171 dirChanges.push_back(eventInfo);
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800172 }
173
Jared Lindbloma9996302013-01-12 00:25:16 -0800174 // delete this file from fileList we have processed it
175 currentState.remove(absFilePath);
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800176 }
177 else
178 {
Jared Lindbloma9996302013-01-12 00:25:16 -0800179 // delete from stored state
180 i.remove();
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800181
182 // this file has been deleted
Jared Lindblom264310d2013-01-12 01:18:58 -0800183 sEventInfo eventInfo;
184 eventInfo.event = DELETED;
Jared Lindblom8278b982013-01-12 15:45:09 -0800185 eventInfo.absFilePath = absFilePath.toStdString();
Jared Lindblom264310d2013-01-12 01:18:58 -0800186 dirChanges.push_back(eventInfo);
Jared Lindbloma9996302013-01-12 00:25:16 -0800187 }
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800188 }
189
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800190 // any files left in fileList have been added
Jared Lindblom8278b982013-01-12 15:45:09 -0800191 for(QHash<QString, qint64>::iterator i = currentState.begin(); i != currentState.end(); ++i)
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800192 {
193 QString absFilePath = i.key();
Jared Lindblom8278b982013-01-12 15:45:09 -0800194 qint64 currentCreated = i.value();
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800195
Jared Lindblom8278b982013-01-12 15:45:09 -0800196 m_storedState.insert(absFilePath, currentCreated);
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800197
198 // this file has been added
Jared Lindblom264310d2013-01-12 01:18:58 -0800199 sEventInfo eventInfo;
200 eventInfo.event = ADDED;
Jared Lindblom8278b982013-01-12 15:45:09 -0800201 eventInfo.absFilePath = absFilePath.toStdString();
Jared Lindblom264310d2013-01-12 01:18:58 -0800202 dirChanges.push_back(eventInfo);
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800203 }
204
Jared Lindbloma9996302013-01-12 00:25:16 -0800205 return dirChanges;
jareda9541812013-01-09 00:08:46 -0800206}
Alexander Afanasyevff731962013-01-09 17:16:28 -0800207
Jared Lindblomf8d32ad2013-01-12 11:32:27 -0800208QByteArray FileSystemWatcher::calcChecksum(QString absFilePath)
209{
210 // initialize checksum
211 QCryptographicHash crypto(QCryptographicHash::Md5);
212
213 // open file
214 QFile file(absFilePath);
215 file.open(QFile::ReadOnly);
216
217 // calculate checksum
218 while(!file.atEnd())
219 {
220 crypto.addData(file.read(8192));
221 }
222
223 return crypto.result();
224}
225
Jared Lindblomc1259722013-01-12 16:47:32 -0800226void FileSystemWatcher::printChanges(std::vector<sEventInfo> dirChanges)
Jared Lindblomf8d32ad2013-01-12 11:32:27 -0800227{
Jared Lindblomc1259722013-01-12 16:47:32 -0800228 if(!dirChanges.empty())
Jared Lindblomf8d32ad2013-01-12 11:32:27 -0800229 {
Jared Lindblomc1259722013-01-12 16:47:32 -0800230 for(size_t i = 0; i < dirChanges.size(); i++)
Jared Lindblomf8d32ad2013-01-12 11:32:27 -0800231 {
232 QString tempString;
233
234 eEvent event = dirChanges[i].event;
Jared Lindblom8278b982013-01-12 15:45:09 -0800235 QString absFilePath = QString::fromStdString(dirChanges[i].absFilePath);
Jared Lindblomf8d32ad2013-01-12 11:32:27 -0800236
237 switch(event)
238 {
239 case ADDED:
240 tempString.append("ADDED: ");
241 break;
242 case MODIFIED:
243 tempString.append("MODIFIED: ");
244 break;
245 case DELETED:
246 tempString.append("DELETED: ");
247 break;
248 }
249
250 tempString.append(absFilePath);
Jared Lindblomf8d32ad2013-01-12 11:32:27 -0800251
Jared Lindblom2eead682013-01-12 20:26:21 -0800252 qDebug() << "\t" << tempString;
253 }
Jared Lindblomf8d32ad2013-01-12 11:32:27 -0800254 }
Jared Lindblomc1259722013-01-12 16:47:32 -0800255 else
256 {
Jared Lindblomc1259722013-01-12 16:47:32 -0800257 qDebug() << "\t[EMPTY]";
Jared Lindblomc1259722013-01-12 16:47:32 -0800258 }
Jared Lindblomf8d32ad2013-01-12 11:32:27 -0800259}
260
Alexander Afanasyevff731962013-01-09 17:16:28 -0800261#if WAF
262#include "filesystemwatcher.moc"
263#include "filesystemwatcher.cpp.moc"
264#endif