blob: a97f8b4fe911fba075fd63f421491ed6bb126dc3 [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())
41 , m_executor (1)
42 , 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
55 m_executor.execute (bind (&FsWatcher::ScanDirectory_Notify_Execute, this, m_dirPath));
Alexander Afanasyev5e140872013-01-24 23:58:10 -080056 m_executor.start ();
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -080057}
58
59FsWatcher::~FsWatcher()
60{
Alexander Afanasyev5e140872013-01-24 23:58:10 -080061 m_executor.shutdown ();
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -080062 delete m_watcher;
63}
64
65void
66FsWatcher::DidDirectoryChanged (QString dirPath)
67{
Alexander Afanasyev69e13172013-01-25 17:16:27 -080068 // _LOG_DEBUG ("Triggered DirPath: " << dirPath.toStdString ());
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -080069
70 m_executor.execute (bind (&FsWatcher::ScanDirectory_Notify_Execute, this, dirPath));
71}
72
73void
74FsWatcher::DidFileChanged (QString filePath)
75{
Alexander Afanasyev69e13172013-01-25 17:16:27 -080076 if (!filePath.startsWith (m_dirPath))
77 {
78 _LOG_ERROR ("Got notification about a file not from the monitored directory");
79 return;
80 }
81 filesystem::path absPathTriggeredFile (filePath.toStdString ());
82 filePath.remove (0, m_dirPath.size ());
83
84 filesystem::path triggeredFile (filePath.toStdString ());
85 if (filesystem::exists (filesystem::path (absPathTriggeredFile)))
86 {
87 _LOG_DEBUG ("Triggered UPDATE of file: " << triggeredFile.relative_path ().generic_string ());
88 m_onChange (triggeredFile.relative_path ());
89 }
90 else
91 {
92 _LOG_DEBUG ("Triggered DELETE of file: " << triggeredFile.relative_path ().generic_string ());
93 m_onDelete (triggeredFile.relative_path ());
94 }
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -080095}
96
97
98void FsWatcher::DidDirectoryChanged_Execute (QString dirPath)
99{
100// // scan directory and populate file list
101// QHash<QString, qint64> currentState = scanDirectory(dirPath);
102
103// // reconcile directory and report changes
104// std::vector<sEventInfo> dirChanges = reconcileDirectory(currentState, dirPath);
105// #ifdef _DEBUG
106// // DEBUG: Print Changes
107// printChanges(dirChanges);
108// #endif
109// // emit the signal if not empty
110// if(!dirChanges.empty())
111// emit dirEventSignal(dirChanges);
112}
113
114void
115FsWatcher::ScanDirectory_Notify_Execute (QString dirPath)
116{
Alexander Afanasyev69e13172013-01-25 17:16:27 -0800117 // exclude working only on last component, not the full path; iterating through all directories, even excluded from monitoring
118 QRegExp exclude ("^(\\.|\\.\\.|\\.chronoshare|.*~|.*\\.swp)$");
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -0800119
120 QDirIterator dirIterator (dirPath,
Alexander Afanasyev69e13172013-01-25 17:16:27 -0800121 QDir::Dirs | QDir::Files | /*QDir::Hidden |*/ QDir::NoSymLinks | QDir::NoDotAndDotDot,
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -0800122 QDirIterator::Subdirectories); // directory iterator (recursive)
123
124 // iterate through directory recursively
125 while (dirIterator.hasNext ())
126 {
127 dirIterator.next ();
128
129 // Get FileInfo
130 QFileInfo fileInfo = dirIterator.fileInfo ();
131
132 QString name = fileInfo.fileName ();
133
134 if (!exclude.exactMatch (name))
135 {
Alexander Afanasyev69e13172013-01-25 17:16:27 -0800136 _LOG_DEBUG ("Not excluded file/dir: " << fileInfo.absoluteFilePath ().toStdString ());
Alexander Afanasyev9e5a4702013-01-24 13:15:23 -0800137 QString absFilePath = fileInfo.absoluteFilePath ();
138
139 // _LOG_DEBUG ("Attempt to add path to watcher: " << absFilePath.toStdString ());
140 m_watcher->addPath (absFilePath);
141
142 if (fileInfo.isFile ())
143 {
144 DidFileChanged (absFilePath);
145 }
146 // // if this is a directory
147 // if(fileInfo.isDir())
148 // {
149 // QStringList dirList = m_watcher->directories();
150
151 // // if the directory is not already being watched
152 // if (absFilePath.startsWith(m_dirPath) && !dirList.contains(absFilePath))
153 // {
154 // _LOG_DEBUG ("Add new dir to watchlist: " << absFilePath.toStdString ());
155 // // add this directory to the watch list
156 // m_watcher->addPath(absFilePath);
157 // }
158 // }
159 // else
160 // {
161 // _LOG_DEBUG ("Found file: " << absFilePath.toStdString ());
162 // // add this file to the file list
163 // // currentState.insert(absFilePath, fileInfo.created().toMSecsSinceEpoch());
164 // }
165 }
166 else
167 {
168 // _LOG_DEBUG ("Excluded file/dir: " << fileInfo.filePath ().toStdString ());
169 }
170 }
171}
172
173// std::vector<sEventInfo> FsWatcher::reconcileDirectory(QHash<QString, qint64> currentState, QString dirPath)
174// {
175// // list of files changed
176// std::vector<sEventInfo> dirChanges;
177
178// // compare result (database/stored snapshot) to fileList (current snapshot)
179// QMutableHashIterator<QString, qint64> i(m_storedState);
180
181// while(i.hasNext())
182// {
183// i.next();
184
185// QString absFilePath = i.key();
186// qint64 storedCreated = i.value();
187
188// // if this file is in a level higher than
189// // this directory, ignore
190// if(!absFilePath.startsWith(dirPath))
191// {
192// continue;
193// }
194
195// // check file existence
196// if(currentState.contains(absFilePath))
197// {
198// qint64 currentCreated = currentState.value(absFilePath);
199
200// if(storedCreated != currentCreated)
201// {
202// // update stored state
203// i.setValue(currentCreated);
204
205// // this file has been modified
206// sEventInfo eventInfo;
207// eventInfo.event = MODIFIED;
208// eventInfo.absFilePath = absFilePath.toStdString();
209// dirChanges.push_back(eventInfo);
210// }
211
212// // delete this file from fileList we have processed it
213// currentState.remove(absFilePath);
214// }
215// else
216// {
217// // delete from stored state
218// i.remove();
219
220// // this file has been deleted
221// sEventInfo eventInfo;
222// eventInfo.event = DELETED;
223// eventInfo.absFilePath = absFilePath.toStdString();
224// dirChanges.push_back(eventInfo);
225// }
226// }
227
228// // any files left in fileList have been added
229// for(QHash<QString, qint64>::iterator i = currentState.begin(); i != currentState.end(); ++i)
230// {
231// QString absFilePath = i.key();
232// qint64 currentCreated = i.value();
233
234// m_storedState.insert(absFilePath, currentCreated);
235
236// // this file has been added
237// sEventInfo eventInfo;
238// eventInfo.event = ADDED;
239// eventInfo.absFilePath = absFilePath.toStdString();
240// dirChanges.push_back(eventInfo);
241// }
242
243// return dirChanges;
244// }
245
246// QByteArray FsWatcher::calcChecksum(QString absFilePath)
247// {
248// // initialize checksum
249// QCryptographicHash crypto(QCryptographicHash::Md5);
250
251// // open file
252// QFile file(absFilePath);
253// file.open(QFile::ReadOnly);
254
255// // calculate checksum
256// while(!file.atEnd())
257// {
258// crypto.addData(file.read(8192));
259// }
260
261// return crypto.result();
262// }
263
264// void FsWatcher::printChanges(std::vector<sEventInfo> dirChanges)
265// {
266// if(!dirChanges.empty())
267// {
268// for(size_t i = 0; i < dirChanges.size(); i++)
269// {
270// QString tempString;
271
272// eEvent event = dirChanges[i].event;
273// QString absFilePath = QString::fromStdString(dirChanges[i].absFilePath);
274
275// switch(event)
276// {
277// case ADDED:
278// tempString.append("ADDED: ");
279// break;
280// case MODIFIED:
281// tempString.append("MODIFIED: ");
282// break;
283// case DELETED:
284// tempString.append("DELETED: ");
285// break;
286// }
287
288// tempString.append(absFilePath);
289
290// _LOG_DEBUG ("\t" << tempString.toStdString ());
291// }
292// }
293// else
294// {
295// _LOG_DEBUG ("\t[EMPTY]");
296// }
297// }
298
299#if WAF
300#include "fs-watcher.moc"
301#include "fs-watcher.cc.moc"
302#endif