xref: /glogg/src/inotifywatchtowerdriver.cpp (revision b0345991179d693b315dfae1d66ad21aa3989113)
1 #include "inotifywatchtowerdriver.h"
2 
3 #include <sys/inotify.h>
4 #include <poll.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 
8 #include "log.h"
9 
10 #include "watchtowerlist.h"
11 
12 INotifyWatchTowerDriver::INotifyWatchTowerDriver() : inotify_fd_( inotify_init() )
13 {
14     int pipefd[2];
15 
16     pipe2( pipefd, O_NONBLOCK );
17 
18     breaking_pipe_read_fd_  = pipefd[0];
19     breaking_pipe_write_fd_ = pipefd[1];
20 }
21 
22 INotifyWatchTowerDriver::~INotifyWatchTowerDriver()
23 {
24     close( breaking_pipe_read_fd_ );
25     close( breaking_pipe_write_fd_ );
26 }
27 
28 INotifyWatchTowerDriver::FileId INotifyWatchTowerDriver::addFile(
29         const std::string& file_name )
30 {
31     // Add a watch for the inode
32     int wd = inotify_add_watch( inotify_fd_, file_name.c_str(),
33             IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF );
34 
35     LOG(logDEBUG) << "INotifyWatchTower::addFile new inotify wd " << wd;
36 
37     return { wd };
38 }
39 
40 INotifyWatchTowerDriver::SymlinkId INotifyWatchTowerDriver::addSymlink(
41         const std::string& file_name )
42 {
43     int symlink_wd = inotify_add_watch( inotify_fd_, file_name.c_str(),
44             IN_DONT_FOLLOW | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF );
45     LOG(logDEBUG) << "INotifyWatchTower::addFile new inotify symlink_wd " << symlink_wd;
46     // (not sure a symlink can be modified but you never know)
47 
48     return { symlink_wd };
49 }
50 
51 INotifyWatchTowerDriver::DirId INotifyWatchTowerDriver::addDir(
52         const std::string& file_name )
53 {
54     int dir_wd = inotify_add_watch( inotify_fd_, file_name.c_str(),
55             IN_CREATE | IN_MOVE | IN_ONLYDIR );
56     LOG(logDEBUG) << "INotifyWatchTower::addFile dir " << file_name
57         << " watched wd " << dir_wd;
58 
59     return { dir_wd };
60 }
61 
62 void INotifyWatchTowerDriver::removeFile(
63         const INotifyWatchTowerDriver::FileId& file_id )
64 {
65     /*
66        LOG(logDEBUG) << "INotifyWatchTower::removeNotification removing inotify wd "
67        << file->file_wd_ << " symlink_wd " << file->symlink_wd_;
68        */
69     if ( file_id.wd_ >= 0 )
70         inotify_rm_watch( inotify_fd_, file_id.wd_ );
71 }
72 
73 void INotifyWatchTowerDriver::removeSymlink( const SymlinkId& symlink_id )
74 {
75     if ( symlink_id.wd_ >= 0 )
76         inotify_rm_watch( inotify_fd_, symlink_id.wd_ );
77 }
78 
79 static const size_t INOTIFY_BUFFER_SIZE = 4096;
80 
81 std::vector<ObservedFile*> INotifyWatchTowerDriver::waitAndProcessEvents(
82         ObservedFileList* list,
83         std::mutex* list_mutex )
84 {
85     std::vector<ObservedFile*> files_to_notify;
86     struct pollfd fds[2];
87 
88     fds[0].fd      = inotify_fd_;
89     fds[0].events  = POLLIN;
90     fds[0].revents = 0;
91 
92     fds[1].fd      = breaking_pipe_read_fd_;
93     fds[1].events  = POLLIN;
94     fds[1].revents = 0;
95 
96     int poll_ret = poll( fds, 2, -1 );
97 
98     if ( poll_ret > 0 )
99     {
100         if ( fds[0].revents & POLLIN )
101         {
102             LOG(logDEBUG4) << "Pollin for inotify";
103             char buffer[ INOTIFY_BUFFER_SIZE ]
104                 __attribute__ ((aligned(__alignof__(struct inotify_event))));
105 
106             ssize_t nb = read( inotify_fd_, buffer, sizeof( buffer ) );
107             if ( nb > 0 )
108             {
109                 size_t offset = 0;
110                 while ( offset < nb ) {
111                     const inotify_event* event =
112                         reinterpret_cast<const inotify_event*>( buffer + offset );
113 
114                     offset += processINotifyEvent( event, list, list_mutex, &files_to_notify );
115                 }
116             }
117             else
118             {
119                 LOG(logWARNING) << "Error reading from inotify " << errno;
120             }
121         }
122 
123         if ( fds[1].revents & POLLIN )
124         {
125             uint8_t byte;
126             read( breaking_pipe_read_fd_, &byte, sizeof byte );
127         }
128     }
129 
130     return files_to_notify;
131 }
132 
133 // Treats the passed event and returns the number of bytes used
134 size_t INotifyWatchTowerDriver::processINotifyEvent(
135         const struct inotify_event* event,
136         ObservedFileList* list,
137         std::mutex* list_mutex,
138         std::vector<ObservedFile*>* files_to_notify )
139 {
140     LOG(logDEBUG4) << "Event received: " << std::hex << event->mask;
141 
142     std::unique_lock<std::mutex> lock( *list_mutex );
143 
144     ObservedFile* file = nullptr;
145 
146     if ( event->mask & ( IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF ) )
147     {
148         LOG(logDEBUG) << "IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF for wd " << event->wd;
149 
150         // Retrieve the file
151         file = list->searchByFileOrSymlinkWd(
152                 { event->wd }, { event->wd } );
153     }
154     else if ( event->mask & ( IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM ) )
155     {
156         LOG(logDEBUG4) << "IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM for wd " << event->wd
157             << " name: " << event->name;
158 
159         // Retrieve the file
160         file = list->searchByDirWdAndName( { event->wd }, event->name );
161 
162         if ( file )
163         {
164             LOG(logDEBUG) << "Dir change for watched file " << event->name;
165         }
166     }
167     else
168     {
169         LOG(logDEBUG) << "Unexpected event: " << event->mask << " wd " << event->wd;
170     }
171 
172     // Call all our observers
173     if ( file )
174     {
175         files_to_notify->push_back( file );
176     }
177 
178     return sizeof( struct inotify_event ) + event->len;
179 }
180 
181 void INotifyWatchTowerDriver::interruptWait()
182 {
183     char byte = 'X';
184 
185     (void) write( breaking_pipe_write_fd_, (void*) &byte, sizeof byte );
186 }
187