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