xref: /glogg/src/inotifywatchtowerdriver.cpp (revision 9ebe83d51f785b947e84c5b8993afea9a272ffd5)
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