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