xref: /glogg/src/inotifywatchtowerdriver.cpp (revision 9f850936f193b6e0057829e50d76cbf76e3a62c7)
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 
INotifyWatchTowerDriver()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 
~INotifyWatchTowerDriver()41 INotifyWatchTowerDriver::~INotifyWatchTowerDriver()
42 {
43     close( breaking_pipe_read_fd_ );
44     close( breaking_pipe_write_fd_ );
45 }
46 
addFile(const std::string & file_name)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 
addSymlink(const std::string & file_name)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 
addDir(const std::string & file_name)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 
removeFile(const INotifyWatchTowerDriver::FileId & file_id)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 
removeSymlink(const SymlinkId & symlink_id)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 
removeDir(const DirId & dir_id)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*>
waitAndProcessEvents(INotifyObservedFileList * list,std::unique_lock<std::mutex> * list_lock,std::vector<INotifyObservedFile * > * files_needing_readding,int timeout_ms)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             LOG(logDEBUG) << "Read " << nb << " bytes";
140             if ( nb > 0 )
141             {
142                 ssize_t offset = 0;
143                 while ( offset < nb ) {
144                     const inotify_event* event =
145                         reinterpret_cast<const inotify_event*>( buffer + offset );
146 
147                     offset += processINotifyEvent( event, list,
148                             &files_to_notify, files_needing_readding );
149                 }
150             }
151             else
152             {
153                 LOG(logWARNING) << "Error reading from inotify " << errno;
154             }
155         }
156 
157         if ( fds[1].revents & POLLIN )
158         {
159             uint8_t byte;
160             read( breaking_pipe_read_fd_, &byte, sizeof byte );
161         }
162     }
163 
164     return files_to_notify;
165 }
166 
167 // Treats the passed event and returns the number of bytes used
processINotifyEvent(const struct inotify_event * event,INotifyObservedFileList * list,std::vector<INotifyObservedFile * > * files_to_notify,std::vector<INotifyObservedFile * > * files_needing_readding)168 size_t INotifyWatchTowerDriver::processINotifyEvent(
169         const struct inotify_event* event,
170         INotifyObservedFileList* list,
171         std::vector<INotifyObservedFile*>* files_to_notify,
172         std::vector<INotifyObservedFile*>* files_needing_readding )
173 {
174     LOG(logDEBUG) << "Event received: 0x" << std::hex << event->mask;
175 
176     INotifyObservedFile* file = nullptr;
177 
178     if ( event->mask & ( IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF ) )
179     {
180         LOG(logDEBUG) << "IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF for wd " << event->wd;
181 
182         // Retrieve the file
183         file = list->searchByFileOrSymlinkWd(
184                 { event->wd }, { event->wd } );
185     }
186     else if ( event->mask & ( IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM ) )
187     {
188         LOG(logDEBUG) << "IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM for wd " << event->wd
189             << " name: " << event->name;
190 
191         // Retrieve the file
192         file = list->searchByDirWdAndName( { event->wd }, event->name );
193 
194         if ( file )
195         {
196             LOG(logDEBUG) << "Dir change for watched file " << event->name;
197             files_needing_readding->push_back( file );
198         }
199     }
200     else
201     {
202         LOG(logDEBUG) << "Unexpected event: " << event->mask << " wd " << event->wd;
203     }
204 
205     if ( file )
206     {
207         LOG(logDEBUG) << "Adding file: " << std::hex << file;
208         files_to_notify->push_back( file );
209     }
210 
211     return sizeof( struct inotify_event ) + event->len;
212 }
213 
interruptWait()214 void INotifyWatchTowerDriver::interruptWait()
215 {
216     char byte = 'X';
217 
218     (void) write( breaking_pipe_write_fd_, (void*) &byte, sizeof byte );
219 }
220