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