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