1 /* Copyright 2012-present Facebook, Inc.
2  * Licensed under the Apache License, Version 2.0 */
3 #pragma once
4 #include "watchman_opendir.h"
5 
6 namespace watchman {
7 class QueryableView;
8 struct InMemoryView;
9 }
10 
11 struct Watcher : public std::enable_shared_from_this<Watcher> {
12   // What's it called??
13   const w_string name;
14 
15   // if this watcher notifies for individual files contained within
16   // a watched dir, false if it only notifies for dirs
17 #define WATCHER_HAS_PER_FILE_NOTIFICATIONS 1
18   // if renames do not reliably report the individual
19   // files renamed in the hierarchy
20 #define WATCHER_COALESCED_RENAME 2
21   unsigned flags;
22 
23   Watcher(const char* name, unsigned flags);
24 
25   // Start up threads or similar.  Called in the context of the
26   // notify thread
27   virtual bool start(const std::shared_ptr<w_root_t>& root);
28 
29   // Perform watcher-specific cleanup for a watched root when it is freed
30   virtual ~Watcher();
31 
32   // Initiate an OS-level watch on the provided file
33   virtual bool startWatchFile(struct watchman_file* file);
34 
35   // Initiate an OS-level watch on the provided dir, return a DIR
36   // handle, or NULL on error
37   virtual std::unique_ptr<watchman_dir_handle> startWatchDir(
38       const std::shared_ptr<w_root_t>& root,
39       struct watchman_dir* dir,
40       struct timeval now,
41       const char* path) = 0;
42 
43   // Signal any threads to terminate.  Do not join them here.
44   virtual void signalThreads();
45 
46   // Consume any available notifications.  If there are none pending,
47   // does not block.
48   virtual bool consumeNotify(
49       const std::shared_ptr<w_root_t>& root,
50       PendingCollection::LockedPtr& coll) = 0;
51 
52   // Wait for an inotify event to become available
53   virtual bool waitNotify(int timeoutms) = 0;
54 };
55 
56 /** Maintains the list of available watchers.
57  * This is fundamentally a map of name -> factory function.
58  * Some watchers (kqueue, inotify) are available on multiple operating
59  * systems: kqueue on OSX and *BSD, inotify on Linux and Solaris.
60  * There are cases where a given watcher is not the preferred mechanism
61  * (eg: inotify is implemented in terms of portfs on Solaris, so we
62  * prefer to target the portfs layer directly), so we have a concept
63  * of priority associated with the watcher.
64  * Larger numbers are higher priority and will be favored when performing
65  * auto-detection.
66  **/
67 class WatcherRegistry {
68  public:
69   WatcherRegistry(
70       const std::string& name,
71       std::function<std::shared_ptr<watchman::QueryableView>(w_root_t*)> init,
72       int priority = 0);
73 
74   /** Locate the appropriate watcher for root and initialize it */
75   static std::shared_ptr<watchman::QueryableView> initWatcher(w_root_t* root);
76 
getName()77   const std::string& getName() const {
78     return name_;
79   }
80 
81  private:
82   std::string name_;
83   std::function<std::shared_ptr<watchman::QueryableView>(w_root_t*)> init_;
84   int pri_;
85 
86   static std::unordered_map<std::string, WatcherRegistry>& getRegistry();
87   static void registerFactory(const WatcherRegistry& factory);
88   static const WatcherRegistry* getWatcherByName(const std::string& name);
89 };
90 
91 /** This template makes it less verbose for the common case of defining
92  * a name -> class mapping in the registry. */
93 template <class WATCHER>
94 class RegisterWatcher : public WatcherRegistry {
95  public:
96   explicit RegisterWatcher(const std::string& name, int priority = 0)
97       : WatcherRegistry(
98             name,
99             [](w_root_t* root) {
100               return std::make_shared<watchman::InMemoryView>(
101                   root, std::make_shared<WATCHER>(root));
102             },
103             priority) {}
104 };
105