1 /* 2 This file is part of the KDE libraries 3 4 SPDX-FileCopyrightText: 1998 Sven Radej <sven@lisa.exp.univie.ac.at> 5 SPDX-FileCopyrightText: 2006 Dirk Mueller <mueller@kde.org> 6 SPDX-FileCopyrightText: 2007 Flavio Castelli <flavio.castelli@gmail.com> 7 SPDX-FileCopyrightText: 2008 Jarosław Staniek <staniek@kde.org> 8 SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org> 9 10 SPDX-License-Identifier: LGPL-2.0-only 11 12 Private Header for class of KDirWatchPrivate 13 this separate header file is needed for MOC processing 14 because KDirWatchPrivate has signals and slots 15 */ 16 17 #ifndef KDIRWATCH_P_H 18 #define KDIRWATCH_P_H 19 20 #include "kdirwatch.h" 21 #include <io/config-kdirwatch.h> 22 23 #ifndef QT_NO_FILESYSTEMWATCHER 24 #define HAVE_QFILESYSTEMWATCHER 1 25 #else 26 #define HAVE_QFILESYSTEMWATCHER 0 27 #endif 28 29 #include <QList> 30 #include <QMap> 31 #include <QObject> 32 #include <QSet> 33 #include <QString> 34 #include <QTimer> 35 class QSocketNotifier; 36 37 #if HAVE_FAM 38 #include <fam.h> 39 #include <limits.h> 40 #endif 41 42 #include <ctime> 43 #include <sys/types.h> // time_t, ino_t 44 45 #define invalid_ctime (static_cast<time_t>(-1)) 46 47 #if HAVE_QFILESYSTEMWATCHER 48 #include <QFileSystemWatcher> 49 #endif // HAVE_QFILESYSTEMWATCHER 50 51 /* KDirWatchPrivate is a singleton and does the watching 52 * for every KDirWatch instance in the application. 53 */ 54 class KDirWatchPrivate : public QObject 55 { 56 Q_OBJECT 57 public: 58 enum entryStatus { 59 Normal = 0, 60 NonExistent, 61 }; 62 enum entryMode { 63 UnknownMode = 0, 64 StatMode, 65 INotifyMode, 66 FAMMode, 67 QFSWatchMode, 68 }; 69 enum { 70 NoChange = 0, 71 Changed = 1, 72 Created = 2, 73 Deleted = 4, 74 }; 75 76 struct Client { ClientClient77 Client(KDirWatch *inst, KDirWatch::WatchModes watchModes) 78 : instance(inst) 79 , count(1) 80 , watchingStopped(inst->isStopped()) 81 , pending(NoChange) 82 , m_watchModes(watchModes) 83 { 84 } 85 86 // The compiler needs a copy ctor for Client when Entry is inserted into m_mapEntries 87 // (even though the vector of clients is empty at that point, so no performance penalty there) 88 // Client(const Client &) = delete; 89 // Client &operator=(const Client &) = delete; 90 // Client(Client &&) = default; 91 // Client &operator=(Client &&) = default; 92 93 KDirWatch *instance; 94 int count; 95 // did the instance stop watching 96 bool watchingStopped; 97 // events blocked when stopped 98 int pending; 99 KDirWatch::WatchModes m_watchModes; 100 }; 101 102 class Entry 103 { 104 public: 105 ~Entry(); 106 // instances interested in events 107 std::vector<Client> m_clients; 108 // nonexistent entries of this directory 109 QList<Entry *> m_entries; 110 QString path; 111 112 // the last observed modification time 113 time_t m_ctime; 114 // last observed inode 115 ino_t m_ino; 116 // the last observed link count 117 int m_nlink; 118 entryStatus m_status; 119 entryMode m_mode; 120 int msecLeft, freq; 121 bool isDir; 122 123 QString parentDirectory() const; 124 void addClient(KDirWatch *, KDirWatch::WatchModes); 125 void removeClient(KDirWatch *); 126 int clientCount() const; isValid()127 bool isValid() 128 { 129 return !m_clients.empty() || !m_entries.empty(); 130 } 131 findSubEntry(const QString & path)132 Entry *findSubEntry(const QString &path) const 133 { 134 for (Entry *sub_entry : std::as_const(m_entries)) { 135 if (sub_entry->path == path) { 136 return sub_entry; 137 } 138 } 139 return nullptr; 140 } 141 142 bool dirty; 143 void propagate_dirty(); 144 145 QList<const Client *> clientsForFileOrDir(const QString &tpath, bool *isDir) const; 146 QList<const Client *> inotifyClientsForFileOrDir(bool isDir) const; 147 148 #if HAVE_FAM 149 FAMRequest fr; 150 bool m_famReportedSeen; 151 #endif 152 153 #if HAVE_SYS_INOTIFY_H 154 int wd; 155 // Creation and Deletion of files happens infrequently, so 156 // can safely be reported as they occur. File changes i.e. those that emit "dirty()" can 157 // happen many times per second, though, so maintain a list of files in this directory 158 // that can be emitted and flushed at the next slotRescan(...). 159 // This will be unused if the Entry is not a directory. 160 QList<QString> m_pendingFileChanges; 161 #endif 162 }; 163 164 typedef QMap<QString, Entry> EntryMap; 165 166 KDirWatchPrivate(); 167 ~KDirWatchPrivate() override; 168 169 void resetList(KDirWatch *instance, bool skippedToo); 170 void useFreq(Entry *e, int newFreq); 171 void addEntry(KDirWatch *instance, const QString &_path, Entry *sub_entry, bool isDir, KDirWatch::WatchModes watchModes = KDirWatch::WatchDirOnly); 172 void removeEntry(KDirWatch *instance, const QString &path, Entry *sub_entry); 173 void removeEntry(KDirWatch *instance, Entry *e, Entry *sub_entry); 174 bool stopEntryScan(KDirWatch *instance, Entry *e); 175 bool restartEntryScan(KDirWatch *instance, Entry *e, bool notify); 176 void stopScan(KDirWatch *instance); 177 void startScan(KDirWatch *instance, bool notify, bool skippedToo); 178 179 void removeEntries(KDirWatch *instance); 180 void statistics(); 181 182 void addWatch(Entry *entry); 183 void removeWatch(Entry *entry); 184 Entry *entry(const QString &_path); 185 int scanEntry(Entry *e); 186 void emitEvent(Entry *e, int event, const QString &fileName = QString()); 187 188 static bool isNoisyFile(const char *filename); 189 190 void ref(); 191 void unref(); 192 193 public Q_SLOTS: 194 void slotRescan(); 195 void famEventReceived(); // for FAM 196 void inotifyEventReceived(); // for inotify 197 void slotRemoveDelayed(); 198 void fswEventReceived(const QString &path); // for QFileSystemWatcher 199 200 public: 201 QTimer timer; 202 EntryMap m_mapEntries; 203 204 KDirWatch::Method m_preferredMethod, m_nfsPreferredMethod; 205 int freq; 206 int statEntries; 207 int m_nfsPollInterval, m_PollInterval; 208 bool useStat(Entry *e); 209 210 // removeList is allowed to contain any entry at most once 211 QSet<Entry *> removeList; 212 bool delayRemove; 213 214 bool rescan_all; 215 QTimer rescan_timer; 216 217 #if HAVE_FAM 218 QSocketNotifier *sn; 219 FAMConnection fc; 220 bool use_fam; 221 222 void checkFAMEvent(FAMEvent *fe); 223 bool useFAM(Entry *e); 224 void disableFAM(); 225 #endif 226 227 #if HAVE_SYS_INOTIFY_H 228 QSocketNotifier *mSn; 229 bool supports_inotify; 230 int m_inotify_fd; 231 QHash<int, Entry *> m_inotify_wd_to_entry; 232 233 bool useINotify(Entry *e); 234 #endif 235 #if HAVE_QFILESYSTEMWATCHER 236 QFileSystemWatcher *fsWatcher; 237 bool useQFSWatch(Entry *e); 238 #endif 239 240 bool _isStopped; 241 242 private: 243 // Public objects that reference this thread-local private instance. 244 uint m_references; 245 }; 246 247 QDebug operator<<(QDebug debug, const KDirWatchPrivate::Entry &entry); 248 249 #endif // KDIRWATCH_P_H 250