xref: /glogg/src/watchtower.cpp (revision c540156c41190af6e419dffb1b2c43c87fdd3cde)
1 #include "watchtower.h"
2 
3 #include <algorithm>
4 
5 #include "log.h"
6 
7 namespace {
8     bool isSymLink( const std::string& file_name );
9     std::string directory_path( const std::string& path );
10 };
11 
12 WatchTower::WatchTower( std::shared_ptr<WatchTowerDriver> driver )
13     : thread_(), driver_( driver ),
14     heartBeat_(std::shared_ptr<void>((void*) 0xDEADC0DE, [] (void*) {}))
15 {
16     running_ = true;
17     thread_ = std::thread( &WatchTower::run, this );
18 }
19 
20 WatchTower::~WatchTower()
21 {
22     running_ = false;
23     thread_.join();
24 }
25 
26 WatchTower::Registration WatchTower::addFile(
27         const std::string& file_name,
28         std::function<void()> notification )
29 {
30     LOG(logDEBUG) << "WatchTower::addFile " << file_name;
31 
32     std::lock_guard<std::mutex> lock( observers_mutex_ );
33 
34     ObservedFile* existing_observed_file =
35         observed_file_list_.searchByName( file_name );
36 
37     std::shared_ptr<std::function<void()>> ptr( new std::function<void()>(std::move( notification ) ) );
38 
39     if ( ! existing_observed_file )
40     {
41         WatchTowerDriver::SymlinkId* symlink_id = nullptr;
42 
43         auto file_id = driver_->addFile( file_name );
44 
45         if ( isSymLink( file_name ) )
46         {
47             // We want to follow the name (as opposed to the inode)
48             // so we watch the symlink as well.
49             symlink_id = driver_->addSymlink( file_name );
50         }
51 
52         auto new_file = observed_file_list_.addNewObservedFile(
53                 ObservedFile( file_name, ptr, wd, symlink_wd ) );
54 
55         auto dir = observed_file_list_.watchedDirectoryForFile( file_name );
56         if ( ! dir )
57         {
58             LOG(logDEBUG) << "INotifyWatchTower::addFile dir for " << file_name
59                 << " not watched, adding...";
60             dir = observed_file_list_.addWatchedDirectoryForFile( file_name );
61 
62             dir->dir_wd_ = driver_->addDir( dir->path );
63         }
64 
65         new_file->dir_ = dir;
66     }
67     else
68     {
69         existing_observed_file->addCallback( ptr );
70     }
71 
72     std::weak_ptr<void> weakHeartBeat(heartBeat_);
73 
74     // Returns a shared pointer that removes its own entry
75     // from the list of watched stuff when it goes out of scope!
76     // Uses a custom deleter to do the work.
77     return std::shared_ptr<void>( 0x0, [this, ptr, weakHeartBeat] (void*) {
78             if ( auto heart_beat = weakHeartBeat.lock() )
79                 removeNotification( this, ptr );
80             } );
81 }
82 
83 //
84 // Private functions
85 //
86 
87 // Called by the dtor for a registration object
88 void WatchTower::removeNotification(
89         WatchTower* watch_tower, std::shared_ptr<void> notification )
90 {
91     LOG(logDEBUG) << "WatchTower::removeNotification";
92 
93     std::lock_guard<std::mutex> lock( watch_tower->observers_mutex_ );
94 
95     auto file =
96         watch_tower->observed_file_list_.removeCallback( notification );
97 
98     if ( file )
99     {
100         driver_->removeFile( file->file_wd_ );
101         driver_->removeSymlink( file->symlink_wd_ );
102     }
103 }
104 
105 // Run in its own thread
106 void INotifyWatchTower::run()
107 {
108     struct pollfd fds[1];
109 
110     fds[0].fd     = inotify_fd;
111     fds[0].events = POLLIN;
112 
113     while ( running_ )
114     {
115         std::vector<ObservedFile*> files = waitAndProcessEvents( observed_file_list_, observers_mutex_ );
116 
117     }
118 }
119 
120 namespace {
121     bool isSymLink( const std::string& file_name )
122     {
123         struct stat buf;
124 
125         lstat( file_name.c_str(), &buf );
126         return ( S_ISLNK(buf.st_mode) );
127     }
128 };
129