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