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