summaryrefslogtreecommitdiff
path: root/distfoldd/watcher.cc
diff options
context:
space:
mode:
Diffstat (limited to 'distfoldd/watcher.cc')
-rw-r--r--distfoldd/watcher.cc132
1 files changed, 132 insertions, 0 deletions
diff --git a/distfoldd/watcher.cc b/distfoldd/watcher.cc
new file mode 100644
index 0000000..664ea58
--- /dev/null
+++ b/distfoldd/watcher.cc
@@ -0,0 +1,132 @@
+#include <QtCore/QDebug>
+
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/inotify.h>
+
+#include "watcher.h"
+
+
+Watcher::Watcher(const QString& path, QObject *parent) :
+ QObject(parent)
+{
+ _fd = inotify_init();
+ fcntl(_fd, F_SETFL, O_NONBLOCK);
+ _buffer.resize(sizeof(struct inotify_event) + NAME_MAX + 1);
+ _mask = IN_CLOSE_WRITE | IN_CREATE | IN_DELETE |
+ IN_MOVED_FROM | IN_MOVED_TO | IN_ONLYDIR;
+
+ _notifier = new QSocketNotifier(_fd, QSocketNotifier::Read, this);
+ connect(_notifier, SIGNAL(activated(int)), SLOT(readInotify()));
+
+ QStringList l = scanDirs(QDir(path));
+ foreach (const QString& s, l) {
+ addWatch(s);
+ }
+}
+
+void Watcher::readInotify()
+{
+ inotify_event *event;
+ const ssize_t struct_size = sizeof(struct inotify_event);
+ ssize_t pos, nread;
+
+ do {
+ nread = read(_fd, _buffer.data(), _buffer.size());
+ if (nread < 0) {
+ int err = errno;
+ if (err == EWOULDBLOCK) return;
+ qWarning() << "Error while reading from inotify" << err;
+ }
+
+ pos = 0;
+ while (pos + struct_size <= nread) {
+ event = reinterpret_cast<inotify_event*>(&(_buffer.data()[pos]));
+ if (event->wd == -1) {
+ // Special treatment
+ // TODO
+ continue;
+ }
+
+ QString path = _watches[event->wd];
+ QString name;
+ QString filePath = path;
+ if (event->len > 0) {
+ name = QString::fromLocal8Bit(event->name);
+ filePath += "/" + name;
+ }
+
+ if (event->mask & (IN_CREATE|IN_MOVED_TO)) {
+ if (event->mask & IN_ISDIR) {
+ // TODO There might already be folders in here.
+ addWatch(filePath);
+ }
+ emit pathAdded(filePath);
+ }
+ if (event->mask & IN_CLOSE_WRITE) {
+ emit pathChanged(filePath);
+ }
+ if (event->mask & (IN_MOVED_FROM|IN_DELETE)) {
+ if (event->mask & IN_ISDIR) {
+ removeWatch(filePath);
+ }
+ emit pathRemoved(filePath);
+ }
+
+ pos += struct_size + event->len;
+ }
+ } while (nread > 0);
+}
+
+QStringList Watcher::scanDirs(const QDir &dir)
+{
+ Q_ASSERT(dir.isReadable());
+ QStringList l(dir.absolutePath());
+ QStringList sub_dirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+
+ foreach (const QString& s, sub_dirs) {
+ const QString abs_path = dir.absoluteFilePath(s);
+ l << scanDirs(QDir(abs_path));
+ }
+
+ return l;
+}
+
+void Watcher::addWatch(const QString& path)
+{
+ int wd = inotify_add_watch(_fd, path.toLocal8Bit().constData(), _mask);
+ if (wd == -1) {
+ qWarning() << "Failed to add watch for" << path;
+ return;
+ }
+ if (_watches.contains(wd)) {
+ qWarning() << "Watch for" << path << "was already setup";
+ return;
+ }
+
+ qDebug() << "Watching" << path;
+ _watches[wd] = path;
+ _dirs[path] = wd;
+}
+
+void Watcher::removeWatch(const QString& path)
+{
+ if (!_dirs.contains(path)) {
+ qWarning() << "Watch for" << path << "not found";
+ return;
+ }
+ int wd = _dirs[path];
+ removeWatch(wd);
+}
+
+void Watcher::removeWatch(int wd)
+{
+ if (!_watches.contains(wd)) {
+ qWarning() << "Watch" << wd << "not found";
+ }
+ QString path = _watches[wd];
+ qDebug() << "Unwatching" << path;
+ inotify_rm_watch(_fd, wd);
+ _dirs.remove(path);
+ _watches.remove(wd);
+}