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