#include #include #include #include #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(&(_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); }