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