File indexing completed on 2024-05-12 03:54:55

0001 /*
0002     This file is part of the KDE libraries
0003 
0004     SPDX-FileCopyrightText: 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
0005     SPDX-FileCopyrightText: 2006 Dirk Mueller <mueller@kde.org>
0006     SPDX-FileCopyrightText: 2007 Flavio Castelli <flavio.castelli@gmail.com>
0007     SPDX-FileCopyrightText: 2008 Jarosław Staniek <staniek@kde.org>
0008     SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org>
0009 
0010     SPDX-License-Identifier: LGPL-2.0-only
0011 
0012     Private Header for class of KDirWatchPrivate
0013     this separate header file is needed for MOC processing
0014     because KDirWatchPrivate has signals and slots
0015 */
0016 
0017 #ifndef KDIRWATCH_P_H
0018 #define KDIRWATCH_P_H
0019 
0020 #include "kdirwatch.h"
0021 #include <io/config-kdirwatch.h>
0022 
0023 #ifndef QT_NO_FILESYSTEMWATCHER
0024 #define HAVE_QFILESYSTEMWATCHER 1
0025 #else
0026 #define HAVE_QFILESYSTEMWATCHER 0
0027 #endif
0028 
0029 #include <QList>
0030 #include <QMap>
0031 #include <QObject>
0032 #include <QSet>
0033 #include <QString>
0034 #include <QTimer>
0035 class QSocketNotifier;
0036 
0037 #include <ctime>
0038 #include <sys/types.h> // time_t, ino_t
0039 
0040 #define invalid_ctime (static_cast<time_t>(-1))
0041 
0042 #if HAVE_QFILESYSTEMWATCHER
0043 #include <QFileSystemWatcher>
0044 #endif // HAVE_QFILESYSTEMWATCHER
0045 
0046 #if HAVE_SYS_INOTIFY_H
0047 struct inotify_event;
0048 #endif
0049 
0050 /* KDirWatchPrivate is a singleton and does the watching
0051  * for every KDirWatch instance in the application.
0052  */
0053 class KDirWatchPrivate : public QObject
0054 {
0055     Q_OBJECT
0056 public:
0057     enum entryStatus {
0058         Normal = 0,
0059         NonExistent,
0060     };
0061     enum entryMode {
0062         UnknownMode = 0,
0063         StatMode,
0064         INotifyMode,
0065         QFSWatchMode,
0066     };
0067     enum {
0068         NoChange = 0,
0069         Changed = 1,
0070         Created = 2,
0071         Deleted = 4,
0072     };
0073 
0074     struct Client {
0075         Client(KDirWatch *inst, KDirWatch::WatchModes watchModes)
0076             : instance(inst)
0077             , count(1)
0078             , watchingStopped(inst->isStopped())
0079             , pending(NoChange)
0080             , m_watchModes(watchModes)
0081         {
0082         }
0083 
0084         // The compiler needs a copy ctor for Client when Entry is inserted into m_mapEntries
0085         // (even though the vector of clients is empty at that point, so no performance penalty there)
0086         // Client(const Client &) = delete;
0087         // Client &operator=(const Client &) = delete;
0088         // Client(Client &&) = default;
0089         // Client &operator=(Client &&) = default;
0090 
0091         KDirWatch *instance;
0092         int count;
0093         // did the instance stop watching
0094         bool watchingStopped;
0095         // events blocked when stopped
0096         int pending;
0097         KDirWatch::WatchModes m_watchModes;
0098     };
0099 
0100     class Entry
0101     {
0102     public:
0103         ~Entry();
0104         // instances interested in events
0105         std::vector<Client> m_clients;
0106         // nonexistent entries of this directory
0107         QList<Entry *> m_entries;
0108         QString path;
0109 
0110         // the last observed modification time
0111         time_t m_ctime;
0112         // last observed inode
0113         ino_t m_ino;
0114         // the last observed link count
0115         int m_nlink;
0116         entryStatus m_status;
0117         entryMode m_mode;
0118         int msecLeft, freq;
0119         bool isDir;
0120 
0121         QString parentDirectory() const;
0122         void addClient(KDirWatch *, KDirWatch::WatchModes);
0123         void removeClient(KDirWatch *);
0124         int clientCount() const;
0125         bool isValid()
0126         {
0127             return !m_clients.empty() || !m_entries.empty();
0128         }
0129 
0130         Entry *findSubEntry(const QString &path) const
0131         {
0132             for (Entry *sub_entry : std::as_const(m_entries)) {
0133                 if (sub_entry->path == path) {
0134                     return sub_entry;
0135                 }
0136             }
0137             return nullptr;
0138         }
0139 
0140         bool dirty;
0141         void propagate_dirty();
0142 
0143         QList<const Client *> clientsForFileOrDir(const QString &tpath, bool *isDir) const;
0144         QList<const Client *> inotifyClientsForFileOrDir(bool isDir) const;
0145 
0146         std::vector<Client>::iterator findInstance(KDirWatch *other)
0147         {
0148             return std::find_if(m_clients.begin(), m_clients.end(), [other](const Client &client) {
0149                 return client.instance == other;
0150             });
0151         }
0152 
0153 #if HAVE_SYS_INOTIFY_H
0154         int wd;
0155         // Creation and Deletion of files happens infrequently, so
0156         // can safely be reported as they occur.  File changes i.e. those that emit "dirty()" can
0157         // happen many times per second, though, so maintain a list of files in this directory
0158         // that can be emitted and flushed at the next slotRescan(...).
0159         // This will be unused if the Entry is not a directory.
0160         QList<QString> m_pendingFileChanges;
0161 #endif
0162     };
0163 
0164     typedef QMap<QString, Entry> EntryMap;
0165 
0166     KDirWatchPrivate();
0167     ~KDirWatchPrivate() override;
0168 
0169     void resetList(KDirWatch *instance, bool skippedToo);
0170     void useFreq(Entry *e, int newFreq);
0171     void addEntry(KDirWatch *instance, const QString &_path, Entry *sub_entry, bool isDir, KDirWatch::WatchModes watchModes = KDirWatch::WatchDirOnly);
0172     void removeEntry(KDirWatch *instance, const QString &path, Entry *sub_entry);
0173     void removeEntry(KDirWatch *instance, Entry *e, Entry *sub_entry);
0174     bool stopEntryScan(KDirWatch *instance, Entry *e);
0175     bool restartEntryScan(KDirWatch *instance, Entry *e, bool notify);
0176     void stopScan(KDirWatch *instance);
0177     void startScan(KDirWatch *instance, bool notify, bool skippedToo);
0178 
0179     void removeEntries(KDirWatch *instance);
0180 
0181     void addWatch(Entry *entry);
0182     void removeWatch(Entry *entry);
0183     Entry *entry(const QString &_path);
0184     int scanEntry(Entry *e);
0185     void emitEvent(Entry *e, int event, const QString &fileName = QString());
0186 
0187     static bool isNoisyFile(const char *filename);
0188 
0189     void ref(KDirWatch *watch);
0190     void unref(KDirWatch *watch);
0191 
0192 #if HAVE_SYS_INOTIFY_H
0193     QString inotifyEventName(const inotify_event *event) const;
0194 #endif
0195 
0196 public Q_SLOTS:
0197     void slotRescan();
0198     void inotifyEventReceived(); // for inotify
0199     void slotRemoveDelayed();
0200     void fswEventReceived(const QString &path); // for QFileSystemWatcher
0201 
0202 public:
0203     QTimer m_statRescanTimer;
0204     EntryMap m_mapEntries;
0205 
0206     KDirWatch::Method m_preferredMethod, m_nfsPreferredMethod;
0207     int freq;
0208     int statEntries;
0209     int m_nfsPollInterval, m_PollInterval;
0210     bool useStat(Entry *e);
0211 
0212     // removeList is allowed to contain any entry at most once
0213     QSet<Entry *> removeList;
0214     bool delayRemove;
0215 
0216     bool rescan_all;
0217     QTimer rescan_timer;
0218 
0219 #if HAVE_SYS_INOTIFY_H
0220     QSocketNotifier *mSn;
0221     bool supports_inotify;
0222     int m_inotify_fd;
0223     QHash<int, Entry *> m_inotify_wd_to_entry;
0224 
0225     bool useINotify(Entry *e);
0226 #endif
0227 #if HAVE_QFILESYSTEMWATCHER
0228     QFileSystemWatcher *fsWatcher;
0229     bool useQFSWatch(Entry *e);
0230 #endif
0231 
0232     bool _isStopped;
0233 
0234 private:
0235     // Public objects that reference this thread-local private instance.
0236     QList<KDirWatch *> m_referencesObjects;
0237 };
0238 
0239 QDebug operator<<(QDebug debug, const KDirWatchPrivate &dwp);
0240 QDebug operator<<(QDebug debug, const KDirWatchPrivate::Entry &entry);
0241 
0242 #endif // KDIRWATCH_P_H