1 /* 2 * Copyright (C) 2015 Nicolas Bonnefon and other contributors 3 * 4 * This file is part of glogg. 5 * 6 * glogg is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * glogg is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with glogg. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "inotifywatchtowerdriver.h" 21 22 #include <sys/inotify.h> 23 #include <poll.h> 24 #include <unistd.h> 25 #include <fcntl.h> 26 27 #include "log.h" 28 29 #include "watchtowerlist.h" 30 31 INotifyWatchTowerDriver::INotifyWatchTowerDriver() : inotify_fd_( inotify_init() ) 32 { 33 int pipefd[2]; 34 35 pipe2( pipefd, O_NONBLOCK ); 36 37 breaking_pipe_read_fd_ = pipefd[0]; 38 breaking_pipe_write_fd_ = pipefd[1]; 39 } 40 41 INotifyWatchTowerDriver::~INotifyWatchTowerDriver() 42 { 43 close( breaking_pipe_read_fd_ ); 44 close( breaking_pipe_write_fd_ ); 45 } 46 47 INotifyWatchTowerDriver::FileId INotifyWatchTowerDriver::addFile( 48 const std::string& file_name ) 49 { 50 // Add a watch for the inode 51 int wd = inotify_add_watch( inotify_fd_, file_name.c_str(), 52 IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF ); 53 54 LOG(logDEBUG) << "INotifyWatchTower::addFile new inotify wd " << wd; 55 56 return { wd }; 57 } 58 59 INotifyWatchTowerDriver::SymlinkId INotifyWatchTowerDriver::addSymlink( 60 const std::string& file_name ) 61 { 62 int symlink_wd = inotify_add_watch( inotify_fd_, file_name.c_str(), 63 IN_DONT_FOLLOW | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF ); 64 LOG(logDEBUG) << "INotifyWatchTower::addFile new inotify symlink_wd " << symlink_wd; 65 // (not sure a symlink can be modified but you never know) 66 67 return { symlink_wd }; 68 } 69 70 INotifyWatchTowerDriver::DirId INotifyWatchTowerDriver::addDir( 71 const std::string& file_name ) 72 { 73 int dir_wd = inotify_add_watch( inotify_fd_, file_name.c_str(), 74 IN_CREATE | IN_MOVE | IN_ONLYDIR ); 75 LOG(logDEBUG) << "INotifyWatchTower::addFile dir " << file_name 76 << " watched wd " << dir_wd; 77 78 return { dir_wd }; 79 } 80 81 void INotifyWatchTowerDriver::removeFile( 82 const INotifyWatchTowerDriver::FileId& file_id ) 83 { 84 /* 85 LOG(logDEBUG) << "INotifyWatchTower::removeNotification removing inotify wd " 86 << file->file_wd_ << " symlink_wd " << file->symlink_wd_; 87 */ 88 if ( file_id.wd_ >= 0 ) 89 inotify_rm_watch( inotify_fd_, file_id.wd_ ); 90 } 91 92 void INotifyWatchTowerDriver::removeSymlink( const SymlinkId& symlink_id ) 93 { 94 if ( symlink_id.wd_ >= 0 ) 95 inotify_rm_watch( inotify_fd_, symlink_id.wd_ ); 96 } 97 98 void INotifyWatchTowerDriver::removeDir( const DirId& dir_id ) 99 { 100 LOG(logDEBUG) << "INotifyWatchTower::removeDir removing inotify wd " << dir_id.wd_; 101 102 if ( dir_id.wd_ >= 0 ) 103 inotify_rm_watch( inotify_fd_, dir_id.wd_ ); 104 } 105 106 static constexpr size_t INOTIFY_BUFFER_SIZE = 4096; 107 108 std::vector<INotifyWatchTowerDriver::INotifyObservedFile*> 109 INotifyWatchTowerDriver::waitAndProcessEvents( 110 INotifyObservedFileList* list, 111 std::unique_lock<std::mutex>* list_lock, 112 std::vector<INotifyObservedFile*>* files_needing_readding, 113 int timeout_ms ) 114 { 115 std::vector<INotifyObservedFile*> files_to_notify; 116 struct pollfd fds[2]; 117 118 fds[0].fd = inotify_fd_; 119 fds[0].events = POLLIN; 120 fds[0].revents = 0; 121 122 fds[1].fd = breaking_pipe_read_fd_; 123 fds[1].events = POLLIN; 124 fds[1].revents = 0; 125 126 list_lock->unlock(); 127 int poll_ret = poll( fds, 2, timeout_ms ? timeout_ms : -1 ); 128 list_lock->lock(); 129 130 if ( poll_ret > 0 ) 131 { 132 if ( fds[0].revents & POLLIN ) 133 { 134 LOG(logDEBUG) << "Pollin for inotify"; 135 char buffer[ INOTIFY_BUFFER_SIZE ] 136 __attribute__ ((aligned(__alignof__(struct inotify_event)))); 137 138 ssize_t nb = read( inotify_fd_, buffer, sizeof( buffer ) ); 139 if ( nb > 0 ) 140 { 141 ssize_t offset = 0; 142 while ( offset < nb ) { 143 const inotify_event* event = 144 reinterpret_cast<const inotify_event*>( buffer + offset ); 145 146 offset += processINotifyEvent( event, list, 147 &files_to_notify, files_needing_readding ); 148 } 149 } 150 else 151 { 152 LOG(logWARNING) << "Error reading from inotify " << errno; 153 } 154 } 155 156 if ( fds[1].revents & POLLIN ) 157 { 158 uint8_t byte; 159 read( breaking_pipe_read_fd_, &byte, sizeof byte ); 160 } 161 } 162 163 return files_to_notify; 164 } 165 166 // Treats the passed event and returns the number of bytes used 167 size_t INotifyWatchTowerDriver::processINotifyEvent( 168 const struct inotify_event* event, 169 INotifyObservedFileList* list, 170 std::vector<INotifyObservedFile*>* files_to_notify, 171 std::vector<INotifyObservedFile*>* files_needing_readding ) 172 { 173 LOG(logDEBUG) << "Event received: " << std::hex << event->mask; 174 175 INotifyObservedFile* file = nullptr; 176 177 if ( event->mask & ( IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF ) ) 178 { 179 LOG(logDEBUG) << "IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF for wd " << event->wd; 180 181 // Retrieve the file 182 file = list->searchByFileOrSymlinkWd( 183 { event->wd }, { event->wd } ); 184 } 185 else if ( event->mask & ( IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM ) ) 186 { 187 LOG(logDEBUG) << "IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM for wd " << event->wd 188 << " name: " << event->name; 189 190 // Retrieve the file 191 file = list->searchByDirWdAndName( { event->wd }, event->name ); 192 193 if ( file ) 194 { 195 LOG(logDEBUG) << "Dir change for watched file " << event->name; 196 files_needing_readding->push_back( file ); 197 } 198 } 199 else 200 { 201 LOG(logDEBUG) << "Unexpected event: " << event->mask << " wd " << event->wd; 202 } 203 204 if ( file ) 205 { 206 LOG(logDEBUG) << "Adding file: " << std::hex << file; 207 files_to_notify->push_back( file ); 208 } 209 210 return sizeof( struct inotify_event ) + event->len; 211 } 212 213 void INotifyWatchTowerDriver::interruptWait() 214 { 215 char byte = 'X'; 216 217 (void) write( breaking_pipe_write_fd_, (void*) &byte, sizeof byte ); 218 } 219