xref: /glogg/src/inotifywatchtowerdriver.cpp (revision 6e2e573c451ec24d720c967fab68538eaa3f3ab5) !
1 #include "inotifywatchtowerdriver.h"
2 
3 #include <sys/inotify.h>
4 #include <poll.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 
8 #include "log.h"
9 
10 #include "watchtowerlist.h"
11 
12 INotifyWatchTowerDriver::INotifyWatchTowerDriver() : inotify_fd_( inotify_init() )
13 {
14     int pipefd[2];
15 
16     pipe2( pipefd, O_NONBLOCK );
17 
18     breaking_pipe_read_fd_  = pipefd[0];
19     breaking_pipe_write_fd_ = pipefd[1];
20 }
21 
22 INotifyWatchTowerDriver::~INotifyWatchTowerDriver()
23 {
24     close( breaking_pipe_read_fd_ );
25     close( breaking_pipe_write_fd_ );
26 }
27 
28 INotifyWatchTowerDriver::FileId INotifyWatchTowerDriver::addFile(
29         const std::string& file_name )
30 {
31     // Add a watch for the inode
32     int wd = inotify_add_watch( inotify_fd_, file_name.c_str(),
33             IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF );
34 
35     LOG(logDEBUG) << "INotifyWatchTower::addFile new inotify wd " << wd;
36 
37     return { wd };
38 }
39 
40 INotifyWatchTowerDriver::SymlinkId INotifyWatchTowerDriver::addSymlink(
41         const std::string& file_name )
42 {
43     int symlink_wd = inotify_add_watch( inotify_fd_, file_name.c_str(),
44             IN_DONT_FOLLOW | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF );
45     LOG(logDEBUG) << "INotifyWatchTower::addFile new inotify symlink_wd " << symlink_wd;
46     // (not sure a symlink can be modified but you never know)
47 
48     return { symlink_wd };
49 }
50 
51 INotifyWatchTowerDriver::DirId INotifyWatchTowerDriver::addDir(
52         const std::string& file_name )
53 {
54     int dir_wd = inotify_add_watch( inotify_fd_, file_name.c_str(),
55             IN_CREATE | IN_MOVE | IN_ONLYDIR );
56     LOG(logDEBUG) << "INotifyWatchTower::addFile dir " << file_name
57         << " watched wd " << dir_wd;
58 
59     return { dir_wd };
60 }
61 
62 void INotifyWatchTowerDriver::removeFile(
63         const INotifyWatchTowerDriver::FileId& file_id )
64 {
65     /*
66        LOG(logDEBUG) << "INotifyWatchTower::removeNotification removing inotify wd "
67        << file->file_wd_ << " symlink_wd " << file->symlink_wd_;
68        */
69     if ( file_id.wd_ >= 0 )
70         inotify_rm_watch( inotify_fd_, file_id.wd_ );
71 }
72 
73 void INotifyWatchTowerDriver::removeSymlink( const SymlinkId& symlink_id )
74 {
75     if ( symlink_id.wd_ >= 0 )
76         inotify_rm_watch( inotify_fd_, symlink_id.wd_ );
77 }
78 
79 void INotifyWatchTowerDriver::removeDir( const DirId& dir_id )
80 {
81    LOG(logDEBUG) << "INotifyWatchTower::removeDir removing inotify wd " << dir_id.wd_;
82 
83     if ( dir_id.wd_ >= 0 )
84         inotify_rm_watch( inotify_fd_, dir_id.wd_ );
85 }
86 
87 static constexpr size_t INOTIFY_BUFFER_SIZE = 4096;
88 
89 std::vector<INotifyWatchTowerDriver::INotifyObservedFile*>
90 INotifyWatchTowerDriver::waitAndProcessEvents(
91         INotifyObservedFileList* list,
92         std::unique_lock<std::mutex>* list_lock,
93         std::vector<INotifyObservedFile*>* files_needing_readding )
94 {
95     std::vector<INotifyObservedFile*> files_to_notify;
96     struct pollfd fds[2];
97 
98     fds[0].fd      = inotify_fd_;
99     fds[0].events  = POLLIN;
100     fds[0].revents = 0;
101 
102     fds[1].fd      = breaking_pipe_read_fd_;
103     fds[1].events  = POLLIN;
104     fds[1].revents = 0;
105 
106     list_lock->unlock();
107     int poll_ret = poll( fds, 2, -1 );
108     list_lock->lock();
109 
110     if ( poll_ret > 0 )
111     {
112         if ( fds[0].revents & POLLIN )
113         {
114             LOG(logDEBUG) << "Pollin for inotify";
115             char buffer[ INOTIFY_BUFFER_SIZE ]
116                 __attribute__ ((aligned(__alignof__(struct inotify_event))));
117 
118             ssize_t nb = read( inotify_fd_, buffer, sizeof( buffer ) );
119             if ( nb > 0 )
120             {
121                 ssize_t offset = 0;
122                 while ( offset < nb ) {
123                     const inotify_event* event =
124                         reinterpret_cast<const inotify_event*>( buffer + offset );
125 
126                     offset += processINotifyEvent( event, list,
127                             &files_to_notify, files_needing_readding );
128                 }
129             }
130             else
131             {
132                 LOG(logWARNING) << "Error reading from inotify " << errno;
133             }
134         }
135 
136         if ( fds[1].revents & POLLIN )
137         {
138             uint8_t byte;
139             read( breaking_pipe_read_fd_, &byte, sizeof byte );
140         }
141     }
142 
143     return files_to_notify;
144 }
145 
146 // Treats the passed event and returns the number of bytes used
147 size_t INotifyWatchTowerDriver::processINotifyEvent(
148         const struct inotify_event* event,
149         INotifyObservedFileList* list,
150         std::vector<INotifyObservedFile*>* files_to_notify,
151         std::vector<INotifyObservedFile*>* files_needing_readding )
152 {
153     LOG(logDEBUG) << "Event received: " << std::hex << event->mask;
154 
155     INotifyObservedFile* file = nullptr;
156 
157     if ( event->mask & ( IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF ) )
158     {
159         LOG(logDEBUG) << "IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF for wd " << event->wd;
160 
161         // Retrieve the file
162         file = list->searchByFileOrSymlinkWd(
163                 { event->wd }, { event->wd } );
164     }
165     else if ( event->mask & ( IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM ) )
166     {
167         LOG(logDEBUG) << "IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM for wd " << event->wd
168             << " name: " << event->name;
169 
170         // Retrieve the file
171         file = list->searchByDirWdAndName( { event->wd }, event->name );
172 
173         if ( file )
174         {
175             LOG(logDEBUG) << "Dir change for watched file " << event->name;
176             files_needing_readding->push_back( file );
177         }
178     }
179     else
180     {
181         LOG(logDEBUG) << "Unexpected event: " << event->mask << " wd " << event->wd;
182     }
183 
184     if ( file )
185     {
186         LOG(logDEBUG) << "Adding file: " << std::hex << file;
187         files_to_notify->push_back( file );
188     }
189 
190     return sizeof( struct inotify_event ) + event->len;
191 }
192 
193 void INotifyWatchTowerDriver::interruptWait()
194 {
195     char byte = 'X';
196 
197     (void) write( breaking_pipe_write_fd_, (void*) &byte, sizeof byte );
198 }
199