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