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 79*3104b268SNicolas Bonnefon void INotifyWatchTowerDriver::removeDir( const DirId& dir_id ) 80*3104b268SNicolas Bonnefon { 81*3104b268SNicolas Bonnefon LOG(logDEBUG) << "INotifyWatchTower::removeDir removing inotify wd " << dir_id.wd_; 82b278d183SNicolas Bonnefon 83*3104b268SNicolas Bonnefon if ( dir_id.wd_ >= 0 ) 84*3104b268SNicolas Bonnefon inotify_rm_watch( inotify_fd_, dir_id.wd_ ); 85*3104b268SNicolas Bonnefon } 86*3104b268SNicolas Bonnefon 87*3104b268SNicolas Bonnefon static constexpr size_t INOTIFY_BUFFER_SIZE = 4096; 88*3104b268SNicolas Bonnefon 89*3104b268SNicolas Bonnefon std::vector<INotifyWatchTowerDriver::INotifyObservedFile*> 90*3104b268SNicolas Bonnefon INotifyWatchTowerDriver::waitAndProcessEvents( 91*3104b268SNicolas Bonnefon INotifyObservedFileList* list, 92*3104b268SNicolas Bonnefon std::unique_lock<std::mutex>* list_lock ) 93c540156cSNicolas Bonnefon { 94f09fa651SNicolas Bonnefon std::vector<INotifyObservedFile*> files_to_notify; 95b0345991SNicolas Bonnefon struct pollfd fds[2]; 96b278d183SNicolas Bonnefon 97b278d183SNicolas Bonnefon fds[0].fd = inotify_fd_; 98b278d183SNicolas Bonnefon fds[0].events = POLLIN; 99c540156cSNicolas Bonnefon fds[0].revents = 0; 100b278d183SNicolas Bonnefon 101b0345991SNicolas Bonnefon fds[1].fd = breaking_pipe_read_fd_; 102b0345991SNicolas Bonnefon fds[1].events = POLLIN; 103b0345991SNicolas Bonnefon fds[1].revents = 0; 104c540156cSNicolas Bonnefon 105*3104b268SNicolas Bonnefon list_lock->unlock(); 106b0345991SNicolas Bonnefon int poll_ret = poll( fds, 2, -1 ); 107*3104b268SNicolas Bonnefon list_lock->lock(); 108b0345991SNicolas Bonnefon 109b0345991SNicolas Bonnefon if ( poll_ret > 0 ) 110b0345991SNicolas Bonnefon { 111b0345991SNicolas Bonnefon if ( fds[0].revents & POLLIN ) 112c540156cSNicolas Bonnefon { 113*3104b268SNicolas Bonnefon LOG(logDEBUG) << "Pollin for inotify"; 114c540156cSNicolas Bonnefon char buffer[ INOTIFY_BUFFER_SIZE ] 115c540156cSNicolas Bonnefon __attribute__ ((aligned(__alignof__(struct inotify_event)))); 116c540156cSNicolas Bonnefon 117b278d183SNicolas Bonnefon ssize_t nb = read( inotify_fd_, buffer, sizeof( buffer ) ); 118c540156cSNicolas Bonnefon if ( nb > 0 ) 119c540156cSNicolas Bonnefon { 120*3104b268SNicolas Bonnefon ssize_t offset = 0; 121c540156cSNicolas Bonnefon while ( offset < nb ) { 122c540156cSNicolas Bonnefon const inotify_event* event = 123c540156cSNicolas Bonnefon reinterpret_cast<const inotify_event*>( buffer + offset ); 124c540156cSNicolas Bonnefon 125*3104b268SNicolas Bonnefon offset += processINotifyEvent( event, list, &files_to_notify ); 126c540156cSNicolas Bonnefon } 127c540156cSNicolas Bonnefon } 128c540156cSNicolas Bonnefon else 129c540156cSNicolas Bonnefon { 130c540156cSNicolas Bonnefon LOG(logWARNING) << "Error reading from inotify " << errno; 131c540156cSNicolas Bonnefon } 132c540156cSNicolas Bonnefon } 133c540156cSNicolas Bonnefon 134b0345991SNicolas Bonnefon if ( fds[1].revents & POLLIN ) 135b0345991SNicolas Bonnefon { 136b0345991SNicolas Bonnefon uint8_t byte; 137b0345991SNicolas Bonnefon read( breaking_pipe_read_fd_, &byte, sizeof byte ); 138b0345991SNicolas Bonnefon } 139b0345991SNicolas Bonnefon } 140b0345991SNicolas Bonnefon 141b278d183SNicolas Bonnefon return files_to_notify; 142b278d183SNicolas Bonnefon } 143b278d183SNicolas Bonnefon 144c540156cSNicolas Bonnefon // Treats the passed event and returns the number of bytes used 145b278d183SNicolas Bonnefon size_t INotifyWatchTowerDriver::processINotifyEvent( 146b278d183SNicolas Bonnefon const struct inotify_event* event, 147f09fa651SNicolas Bonnefon INotifyWatchTowerDriver::INotifyObservedFileList* list, 148f09fa651SNicolas Bonnefon std::vector<INotifyWatchTowerDriver::INotifyObservedFile*>* files_to_notify ) 149c540156cSNicolas Bonnefon { 150*3104b268SNicolas Bonnefon LOG(logDEBUG) << "Event received: " << std::hex << event->mask; 151c540156cSNicolas Bonnefon 152f09fa651SNicolas Bonnefon INotifyObservedFile* file = nullptr; 153c540156cSNicolas Bonnefon 154c540156cSNicolas Bonnefon if ( event->mask & ( IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF ) ) 155c540156cSNicolas Bonnefon { 156c540156cSNicolas Bonnefon LOG(logDEBUG) << "IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF for wd " << event->wd; 157c540156cSNicolas Bonnefon 158c540156cSNicolas Bonnefon // Retrieve the file 159b278d183SNicolas Bonnefon file = list->searchByFileOrSymlinkWd( 160b278d183SNicolas Bonnefon { event->wd }, { event->wd } ); 161c540156cSNicolas Bonnefon } 162c540156cSNicolas Bonnefon else if ( event->mask & ( IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM ) ) 163c540156cSNicolas Bonnefon { 164b0345991SNicolas Bonnefon LOG(logDEBUG4) << "IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM for wd " << event->wd 165c540156cSNicolas Bonnefon << " name: " << event->name; 166c540156cSNicolas Bonnefon 167c540156cSNicolas Bonnefon // Retrieve the file 168b278d183SNicolas Bonnefon file = list->searchByDirWdAndName( { event->wd }, event->name ); 169c540156cSNicolas Bonnefon 170c540156cSNicolas Bonnefon if ( file ) 171c540156cSNicolas Bonnefon { 172c540156cSNicolas Bonnefon LOG(logDEBUG) << "Dir change for watched file " << event->name; 173c540156cSNicolas Bonnefon } 174c540156cSNicolas Bonnefon } 175c540156cSNicolas Bonnefon else 176c540156cSNicolas Bonnefon { 177c540156cSNicolas Bonnefon LOG(logDEBUG) << "Unexpected event: " << event->mask << " wd " << event->wd; 178c540156cSNicolas Bonnefon } 179c540156cSNicolas Bonnefon 180c540156cSNicolas Bonnefon if ( file ) 181c540156cSNicolas Bonnefon { 182*3104b268SNicolas Bonnefon LOG(logDEBUG) << "Adding file: " << std::hex << file; 183b278d183SNicolas Bonnefon files_to_notify->push_back( file ); 184c540156cSNicolas Bonnefon } 185c540156cSNicolas Bonnefon 186c540156cSNicolas Bonnefon return sizeof( struct inotify_event ) + event->len; 187c540156cSNicolas Bonnefon } 188b0345991SNicolas Bonnefon 189b0345991SNicolas Bonnefon void INotifyWatchTowerDriver::interruptWait() 190b0345991SNicolas Bonnefon { 191b0345991SNicolas Bonnefon char byte = 'X'; 192b0345991SNicolas Bonnefon 193b0345991SNicolas Bonnefon (void) write( breaking_pipe_write_fd_, (void*) &byte, sizeof byte ); 194b0345991SNicolas Bonnefon } 195