diff options
author | Daniel Molkentin <danimo@owncloud.com> | 2014-07-11 02:31:24 +0400 |
---|---|---|
committer | Daniel Molkentin <danimo@owncloud.com> | 2014-07-11 13:07:31 +0400 |
commit | df3c3bca025a7cdb5f20e55fc2ecc37618e7cc8d (patch) | |
tree | 22fa58b5a09ec9f93ad376dce2edd6272483fc1a /src/gui/folderwatcher_linux.cpp | |
parent | d1b991e1984ef0c4ed803c5c5ead1ce3bfe00266 (diff) |
Split into three separate projects: library, gui and cmd
Diffstat (limited to 'src/gui/folderwatcher_linux.cpp')
-rw-r--r-- | src/gui/folderwatcher_linux.cpp | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/src/gui/folderwatcher_linux.cpp b/src/gui/folderwatcher_linux.cpp new file mode 100644 index 000000000..4d75047dc --- /dev/null +++ b/src/gui/folderwatcher_linux.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (C) by Klaas Freitag <freitag@owncloud.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "config.h" + +#include <sys/inotify.h> + +#include "folder.h" +#include "folderwatcher_linux.h" + +#include <cerrno> +#include <QDebug> +#include <QStringList> +#include <QObject> +#include <QVarLengthArray> + +namespace Mirall { + +FolderWatcherPrivate::FolderWatcherPrivate(FolderWatcher *p, const QString& path) + : QObject(), + _parent(p), + _folder(path) +{ + _fd = inotify_init(); + if (_fd != -1) { + _socket.reset( new QSocketNotifier(_fd, QSocketNotifier::Read) ); + connect(_socket.data(), SIGNAL(activated(int)), SLOT(slotReceivedNotification(int))); + } else { + qDebug() << Q_FUNC_INFO << "notify_init() failed: " << strerror(errno); + } + + QMetaObject::invokeMethod(this, "slotAddFolderRecursive", Q_ARG(QString, path)); + +} + +FolderWatcherPrivate::~FolderWatcherPrivate() +{ + +} + +// attention: result list passed by reference! +bool FolderWatcherPrivate::findFoldersBelow( const QDir& dir, QStringList& fullList ) +{ + bool ok = true; + if( !(dir.exists() && dir.isReadable()) ) { + qDebug() << "Non existing path coming in: " << dir.absolutePath(); + ok = false; + } else { + QStringList nameFilter; + nameFilter << QLatin1String("*"); + QDir::Filters filter = QDir::Dirs | QDir::NoDotAndDotDot|QDir::NoSymLinks; + const QStringList pathes = dir.entryList(nameFilter, filter); + + QStringList::const_iterator constIterator; + for (constIterator = pathes.constBegin(); constIterator != pathes.constEnd(); + ++constIterator) { + const QString fullPath(dir.path()+QLatin1String("/")+(*constIterator)); + fullList.append(fullPath); + ok = findFoldersBelow(QDir(fullPath), fullList); + } + } + + return ok; +} + +void FolderWatcherPrivate::inotifyRegisterPath(const QString& path) +{ + if( !path.isEmpty()) { + int wd = inotify_add_watch(_fd, path.toUtf8().constData(), + IN_CLOSE_WRITE | IN_ATTRIB | IN_MOVE | + IN_CREATE |IN_DELETE | IN_DELETE_SELF | + IN_MOVE_SELF |IN_UNMOUNT |IN_ONLYDIR | + IN_DONT_FOLLOW ); + if( wd > -1 ) { + _watches.insert(wd, path); + } + } +} + +void FolderWatcherPrivate::slotAddFolderRecursive(const QString &path) +{ + int subdirs = 0; + qDebug() << "(+) Watcher:" << path; + + QDir inPath(path); + inotifyRegisterPath(inPath.absolutePath()); + + const QStringList watchedFolders = _watches.values(); + + QStringList allSubfolders; + if( !findFoldersBelow(QDir(path), allSubfolders)) { + qDebug() << "Could not traverse all sub folders"; + } + // qDebug() << "currently watching " << watchedFolders; + QStringListIterator subfoldersIt(allSubfolders); + while (subfoldersIt.hasNext()) { + QString subfolder = subfoldersIt.next(); + // qDebug() << " (**) subfolder: " << subfolder; + QDir folder (subfolder); + if (folder.exists() && !watchedFolders.contains(folder.absolutePath())) { + subdirs++; + if( _parent->pathIsIgnored(subfolder) ) { + qDebug() << "* Not adding" << folder.path(); + continue; + } + inotifyRegisterPath(folder.absolutePath()); + } else { + qDebug() << " `-> discarded:" << folder.path(); + } + } + + if (subdirs >0) { + qDebug() << " `-> and" << subdirs << "subdirectories"; + } +} + +void FolderWatcherPrivate::slotReceivedNotification(int fd) +{ + int len; + struct inotify_event* event; + int i; + int error; + QVarLengthArray<char, 2048> buffer(2048); + + do { + len = read(fd, buffer.data(), buffer.size()); + error = errno; + /** + * From inotify documentation: + * + * The behavior when the buffer given to read(2) is too + * small to return information about the next event + * depends on the kernel version: in kernels before 2.6.21, + * read(2) returns 0; since kernel 2.6.21, read(2) fails with + * the error EINVAL. + */ + if (len < 0 && error == EINVAL) + { + // double the buffer size + buffer.resize(buffer.size() * 2); + /* and try again ... */ + continue; + } + } while (false); + + // reset counter + i = 0; + // while there are enough events in the buffer + while(i + sizeof(struct inotify_event) < static_cast<unsigned int>(len)) { + // cast an inotify_event + event = (struct inotify_event*)&buffer[i]; + // with the help of watch descriptor, retrieve, corresponding INotify + if (event == NULL) { + qDebug() << "NULL event"; + i += sizeof(struct inotify_event); + continue; + } + + // fire event + // Note: The name of the changed file and stuff could be taken from + // the event data structure. That does not happen yet. + if (event->len > 0 && event->wd > -1) { + // qDebug() << Q_FUNC_INFO << event->name; + if (QByteArray(event->name).startsWith(".csync") || + QByteArray(event->name).startsWith(".owncloudsync.log")) { + // qDebug() << "ignore journal"; + } else { + const QString p = _watches[event->wd]; + _parent->changeDetected(p); + } + } + + // increment counter + i += sizeof(struct inotify_event) + event->len; + } + +} + +void FolderWatcherPrivate::addPath(const QString& path) +{ + slotAddFolderRecursive(path); +} + +void FolderWatcherPrivate::removePath(const QString& path) +{ + int wid = -1; + // Remove the inotify watch. + QHash<int, QString>::const_iterator i = _watches.constBegin(); + + while (i != _watches.constEnd()) { + if( i.value() == path ) { + wid = i.key(); + break; + } + ++i; + } + if( wid > -1 ) { + inotify_rm_watch(_fd, wid); + _watches.remove(wid); + } +} + +} // ns mirall |