blob: 94c6b3f7a694c65b40db2f9f6c8aa11054a1d363 [file] [log] [blame]
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -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 * Alexander Afanasyev <alexander.afanasyev@ucla.edu>
20 * Zhenkai Zhu <zhenkai@cs.ucla.edu>
21 */
22
23#include "fs-watcher.h"
24#include "logging.h"
25
26#include <boost/bind.hpp>
27
28#include <QDirIterator>
29#include <QRegExp>
30
31using namespace std;
32using namespace boost;
33
34INIT_LOGGER ("FsWatcher");
35
Alexander Afanasyev9ca444e2013-01-25 16:29:35 -080036FsWatcher::FsWatcher (QString dirPath,
37 LocalFile_Change_Callback onChange, LocalFile_Change_Callback onDelete,
38 QObject* parent)
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -080039 : QObject(parent)
40 , m_watcher (new QFileSystemWatcher())
Alexander Afanasyev583449a2013-01-28 17:04:06 -080041 , m_scheduler (new Scheduler ())
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -080042 , m_dirPath (dirPath)
Alexander Afanasyev9ca444e2013-01-25 16:29:35 -080043 , m_onChange (onChange)
44 , m_onDelete (onDelete)
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -080045{
46 _LOG_DEBUG ("Monitor dir: " << m_dirPath.toStdString ());
47 // add main directory to monitor
Alexander Afanasyev69e13172013-01-25 17:16:27 -080048
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -080049 m_watcher->addPath (m_dirPath);
50
51 // register signals (callback functions)
52 connect (m_watcher, SIGNAL (directoryChanged (QString)), this, SLOT (DidDirectoryChanged (QString)));
53 connect (m_watcher, SIGNAL (fileChanged (QString)), this, SLOT (DidFileChanged (QString)));
54
Alexander Afanasyev583449a2013-01-28 17:04:06 -080055 m_scheduler->start ();
56
57 Scheduler::scheduleOneTimeTask (m_scheduler, 0.1,
58 bind (&FsWatcher::ScanDirectory_Notify_Execute, this, m_dirPath),
59 m_dirPath.toStdString ()); // only one task will be scheduled per directory
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -080060}
61
62FsWatcher::~FsWatcher()
63{
Alexander Afanasyev583449a2013-01-28 17:04:06 -080064 m_scheduler->shutdown ();
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -080065}
66
67void
68FsWatcher::DidDirectoryChanged (QString dirPath)
69{
Alexander Afanasyev583449a2013-01-28 17:04:06 -080070 _LOG_DEBUG ("Triggered DirPath: " << dirPath.toStdString ());
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -080071
Alexander Afanasyev583449a2013-01-28 17:04:06 -080072 // m_executor.execute (bind (&FsWatcher::ScanDirectory_Notify_Execute, this, dirPath));
73 Scheduler::scheduleOneTimeTask (m_scheduler, 0.5,
74 bind (&FsWatcher::ScanDirectory_Notify_Execute, this, dirPath),
75 dirPath.toStdString ()); // only one task will be scheduled per directory
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -080076}
77
78void
79FsWatcher::DidFileChanged (QString filePath)
80{
Alexander Afanasyev69e13172013-01-25 17:16:27 -080081 if (!filePath.startsWith (m_dirPath))
82 {
83 _LOG_ERROR ("Got notification about a file not from the monitored directory");
84 return;
85 }
86 filesystem::path absPathTriggeredFile (filePath.toStdString ());
87 filePath.remove (0, m_dirPath.size ());
88
89 filesystem::path triggeredFile (filePath.toStdString ());
90 if (filesystem::exists (filesystem::path (absPathTriggeredFile)))
91 {
92 _LOG_DEBUG ("Triggered UPDATE of file: " << triggeredFile.relative_path ().generic_string ());
Alexander Afanasyev583449a2013-01-28 17:04:06 -080093 // m_onChange (triggeredFile.relative_path ());
94
95 Scheduler::scheduleOneTimeTask (m_scheduler, 0.5,
96 bind (m_onChange, triggeredFile.relative_path ()),
97 triggeredFile.relative_path ().string());
Alexander Afanasyev69e13172013-01-25 17:16:27 -080098 }
99 else
100 {
101 _LOG_DEBUG ("Triggered DELETE of file: " << triggeredFile.relative_path ().generic_string ());
Alexander Afanasyev583449a2013-01-28 17:04:06 -0800102 // m_onDelete (triggeredFile.relative_path ());
103
104 Scheduler::scheduleOneTimeTask (m_scheduler, 0.5,
105 bind (m_onDelete, triggeredFile.relative_path ()),
106 triggeredFile.relative_path ().string());
Alexander Afanasyev69e13172013-01-25 17:16:27 -0800107 }
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -0800108}
109
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -0800110void
111FsWatcher::ScanDirectory_Notify_Execute (QString dirPath)
112{
Alexander Afanasyev69e13172013-01-25 17:16:27 -0800113 // exclude working only on last component, not the full path; iterating through all directories, even excluded from monitoring
114 QRegExp exclude ("^(\\.|\\.\\.|\\.chronoshare|.*~|.*\\.swp)$");
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -0800115
116 QDirIterator dirIterator (dirPath,
Alexander Afanasyev69e13172013-01-25 17:16:27 -0800117 QDir::Dirs | QDir::Files | /*QDir::Hidden |*/ QDir::NoSymLinks | QDir::NoDotAndDotDot,
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -0800118 QDirIterator::Subdirectories); // directory iterator (recursive)
119
120 // iterate through directory recursively
121 while (dirIterator.hasNext ())
122 {
123 dirIterator.next ();
124
125 // Get FileInfo
126 QFileInfo fileInfo = dirIterator.fileInfo ();
127
128 QString name = fileInfo.fileName ();
129
130 if (!exclude.exactMatch (name))
131 {
Alexander Afanasyev69e13172013-01-25 17:16:27 -0800132 _LOG_DEBUG ("Not excluded file/dir: " << fileInfo.absoluteFilePath ().toStdString ());
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -0800133 QString absFilePath = fileInfo.absoluteFilePath ();
134
135 // _LOG_DEBUG ("Attempt to add path to watcher: " << absFilePath.toStdString ());
136 m_watcher->addPath (absFilePath);
137
138 if (fileInfo.isFile ())
139 {
140 DidFileChanged (absFilePath);
141 }
142 // // if this is a directory
143 // if(fileInfo.isDir())
144 // {
145 // QStringList dirList = m_watcher->directories();
146
147 // // if the directory is not already being watched
148 // if (absFilePath.startsWith(m_dirPath) && !dirList.contains(absFilePath))
149 // {
150 // _LOG_DEBUG ("Add new dir to watchlist: " << absFilePath.toStdString ());
151 // // add this directory to the watch list
152 // m_watcher->addPath(absFilePath);
153 // }
154 // }
155 // else
156 // {
157 // _LOG_DEBUG ("Found file: " << absFilePath.toStdString ());
158 // // add this file to the file list
159 // // currentState.insert(absFilePath, fileInfo.created().toMSecsSinceEpoch());
160 // }
161 }
162 else
163 {
164 // _LOG_DEBUG ("Excluded file/dir: " << fileInfo.filePath ().toStdString ());
165 }
166 }
167}
168
169// std::vector<sEventInfo> FsWatcher::reconcileDirectory(QHash<QString, qint64> currentState, QString dirPath)
170// {
171// // list of files changed
172// std::vector<sEventInfo> dirChanges;
173
174// // compare result (database/stored snapshot) to fileList (current snapshot)
175// QMutableHashIterator<QString, qint64> i(m_storedState);
176
177// while(i.hasNext())
178// {
179// i.next();
180
181// QString absFilePath = i.key();
182// qint64 storedCreated = i.value();
183
184// // if this file is in a level higher than
185// // this directory, ignore
186// if(!absFilePath.startsWith(dirPath))
187// {
188// continue;
189// }
190
191// // check file existence
192// if(currentState.contains(absFilePath))
193// {
194// qint64 currentCreated = currentState.value(absFilePath);
195
196// if(storedCreated != currentCreated)
197// {
198// // update stored state
199// i.setValue(currentCreated);
200
201// // this file has been modified
202// sEventInfo eventInfo;
203// eventInfo.event = MODIFIED;
204// eventInfo.absFilePath = absFilePath.toStdString();
205// dirChanges.push_back(eventInfo);
206// }
207
208// // delete this file from fileList we have processed it
209// currentState.remove(absFilePath);
210// }
211// else
212// {
213// // delete from stored state
214// i.remove();
215
216// // this file has been deleted
217// sEventInfo eventInfo;
218// eventInfo.event = DELETED;
219// eventInfo.absFilePath = absFilePath.toStdString();
220// dirChanges.push_back(eventInfo);
221// }
222// }
223
224// // any files left in fileList have been added
225// for(QHash<QString, qint64>::iterator i = currentState.begin(); i != currentState.end(); ++i)
226// {
227// QString absFilePath = i.key();
228// qint64 currentCreated = i.value();
229
230// m_storedState.insert(absFilePath, currentCreated);
231
232// // this file has been added
233// sEventInfo eventInfo;
234// eventInfo.event = ADDED;
235// eventInfo.absFilePath = absFilePath.toStdString();
236// dirChanges.push_back(eventInfo);
237// }
238
239// return dirChanges;
240// }
241
242// QByteArray FsWatcher::calcChecksum(QString absFilePath)
243// {
244// // initialize checksum
245// QCryptographicHash crypto(QCryptographicHash::Md5);
246
247// // open file
248// QFile file(absFilePath);
249// file.open(QFile::ReadOnly);
250
251// // calculate checksum
252// while(!file.atEnd())
253// {
254// crypto.addData(file.read(8192));
255// }
256
257// return crypto.result();
258// }
259
260// void FsWatcher::printChanges(std::vector<sEventInfo> dirChanges)
261// {
262// if(!dirChanges.empty())
263// {
264// for(size_t i = 0; i < dirChanges.size(); i++)
265// {
266// QString tempString;
267
268// eEvent event = dirChanges[i].event;
269// QString absFilePath = QString::fromStdString(dirChanges[i].absFilePath);
270
271// switch(event)
272// {
273// case ADDED:
274// tempString.append("ADDED: ");
275// break;
276// case MODIFIED:
277// tempString.append("MODIFIED: ");
278// break;
279// case DELETED:
280// tempString.append("DELETED: ");
281// break;
282// }
283
284// tempString.append(absFilePath);
285
286// _LOG_DEBUG ("\t" << tempString.toStdString ());
287// }
288// }
289// else
290// {
291// _LOG_DEBUG ("\t[EMPTY]");
292// }
293// }
294
295#if WAF
296#include "fs-watcher.moc"
297#include "fs-watcher.cc.moc"
298#endif