19ebe83d5SNicolas Bonnefon /*
29ebe83d5SNicolas Bonnefon * Copyright (C) 2015 Nicolas Bonnefon and other contributors
39ebe83d5SNicolas Bonnefon *
49ebe83d5SNicolas Bonnefon * This file is part of glogg.
59ebe83d5SNicolas Bonnefon *
69ebe83d5SNicolas Bonnefon * glogg is free software: you can redistribute it and/or modify
79ebe83d5SNicolas Bonnefon * it under the terms of the GNU General Public License as published by
89ebe83d5SNicolas Bonnefon * the Free Software Foundation, either version 3 of the License, or
99ebe83d5SNicolas Bonnefon * (at your option) any later version.
109ebe83d5SNicolas Bonnefon *
119ebe83d5SNicolas Bonnefon * glogg is distributed in the hope that it will be useful,
129ebe83d5SNicolas Bonnefon * but WITHOUT ANY WARRANTY; without even the implied warranty of
139ebe83d5SNicolas Bonnefon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
149ebe83d5SNicolas Bonnefon * GNU General Public License for more details.
159ebe83d5SNicolas Bonnefon *
169ebe83d5SNicolas Bonnefon * You should have received a copy of the GNU General Public License
179ebe83d5SNicolas Bonnefon * along with glogg. If not, see <http://www.gnu.org/licenses/>.
189ebe83d5SNicolas Bonnefon */
199ebe83d5SNicolas 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
INotifyWatchTowerDriver()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
~INotifyWatchTowerDriver()41b0345991SNicolas Bonnefon INotifyWatchTowerDriver::~INotifyWatchTowerDriver()
42b0345991SNicolas Bonnefon {
43b0345991SNicolas Bonnefon close( breaking_pipe_read_fd_ );
44b0345991SNicolas Bonnefon close( breaking_pipe_write_fd_ );
45c540156cSNicolas Bonnefon }
46c540156cSNicolas Bonnefon
addFile(const std::string & file_name)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
addSymlink(const std::string & file_name)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
addDir(const std::string & file_name)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
removeFile(const INotifyWatchTowerDriver::FileId & file_id)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
removeSymlink(const SymlinkId & symlink_id)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
removeDir(const DirId & dir_id)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*>
waitAndProcessEvents(INotifyObservedFileList * list,std::unique_lock<std::mutex> * list_lock,std::vector<INotifyObservedFile * > * files_needing_readding,int timeout_ms)1093104b268SNicolas Bonnefon INotifyWatchTowerDriver::waitAndProcessEvents(
1103104b268SNicolas Bonnefon INotifyObservedFileList* list,
11191f7c705SNicolas Bonnefon std::unique_lock<std::mutex>* list_lock,
1124fc8b18aSNicolas Bonnefon std::vector<INotifyObservedFile*>* files_needing_readding,
1134fc8b18aSNicolas Bonnefon int timeout_ms )
114c540156cSNicolas Bonnefon {
115f09fa651SNicolas Bonnefon std::vector<INotifyObservedFile*> files_to_notify;
116b0345991SNicolas Bonnefon struct pollfd fds[2];
117b278d183SNicolas Bonnefon
118b278d183SNicolas Bonnefon fds[0].fd = inotify_fd_;
119b278d183SNicolas Bonnefon fds[0].events = POLLIN;
120c540156cSNicolas Bonnefon fds[0].revents = 0;
121b278d183SNicolas Bonnefon
122b0345991SNicolas Bonnefon fds[1].fd = breaking_pipe_read_fd_;
123b0345991SNicolas Bonnefon fds[1].events = POLLIN;
124b0345991SNicolas Bonnefon fds[1].revents = 0;
125c540156cSNicolas Bonnefon
1263104b268SNicolas Bonnefon list_lock->unlock();
1274fc8b18aSNicolas Bonnefon int poll_ret = poll( fds, 2, timeout_ms ? timeout_ms : -1 );
1283104b268SNicolas Bonnefon list_lock->lock();
129b0345991SNicolas Bonnefon
130b0345991SNicolas Bonnefon if ( poll_ret > 0 )
131b0345991SNicolas Bonnefon {
132b0345991SNicolas Bonnefon if ( fds[0].revents & POLLIN )
133c540156cSNicolas Bonnefon {
1343104b268SNicolas Bonnefon LOG(logDEBUG) << "Pollin for inotify";
135c540156cSNicolas Bonnefon char buffer[ INOTIFY_BUFFER_SIZE ]
136c540156cSNicolas Bonnefon __attribute__ ((aligned(__alignof__(struct inotify_event))));
137c540156cSNicolas Bonnefon
138b278d183SNicolas Bonnefon ssize_t nb = read( inotify_fd_, buffer, sizeof( buffer ) );
139*9f850936SNicolas Bonnefon LOG(logDEBUG) << "Read " << nb << " bytes";
140c540156cSNicolas Bonnefon if ( nb > 0 )
141c540156cSNicolas Bonnefon {
1423104b268SNicolas Bonnefon ssize_t offset = 0;
143c540156cSNicolas Bonnefon while ( offset < nb ) {
144c540156cSNicolas Bonnefon const inotify_event* event =
145c540156cSNicolas Bonnefon reinterpret_cast<const inotify_event*>( buffer + offset );
146c540156cSNicolas Bonnefon
14791f7c705SNicolas Bonnefon offset += processINotifyEvent( event, list,
14891f7c705SNicolas Bonnefon &files_to_notify, files_needing_readding );
149c540156cSNicolas Bonnefon }
150c540156cSNicolas Bonnefon }
151c540156cSNicolas Bonnefon else
152c540156cSNicolas Bonnefon {
153c540156cSNicolas Bonnefon LOG(logWARNING) << "Error reading from inotify " << errno;
154c540156cSNicolas Bonnefon }
155c540156cSNicolas Bonnefon }
156c540156cSNicolas Bonnefon
157b0345991SNicolas Bonnefon if ( fds[1].revents & POLLIN )
158b0345991SNicolas Bonnefon {
159b0345991SNicolas Bonnefon uint8_t byte;
160b0345991SNicolas Bonnefon read( breaking_pipe_read_fd_, &byte, sizeof byte );
161b0345991SNicolas Bonnefon }
162b0345991SNicolas Bonnefon }
163b0345991SNicolas Bonnefon
164b278d183SNicolas Bonnefon return files_to_notify;
165b278d183SNicolas Bonnefon }
166b278d183SNicolas Bonnefon
167c540156cSNicolas Bonnefon // 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)168b278d183SNicolas Bonnefon size_t INotifyWatchTowerDriver::processINotifyEvent(
169b278d183SNicolas Bonnefon const struct inotify_event* event,
17091f7c705SNicolas Bonnefon INotifyObservedFileList* list,
17191f7c705SNicolas Bonnefon std::vector<INotifyObservedFile*>* files_to_notify,
17291f7c705SNicolas Bonnefon std::vector<INotifyObservedFile*>* files_needing_readding )
173c540156cSNicolas Bonnefon {
174*9f850936SNicolas Bonnefon LOG(logDEBUG) << "Event received: 0x" << std::hex << event->mask;
175c540156cSNicolas Bonnefon
176f09fa651SNicolas Bonnefon INotifyObservedFile* file = nullptr;
177c540156cSNicolas Bonnefon
178c540156cSNicolas Bonnefon if ( event->mask & ( IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF ) )
179c540156cSNicolas Bonnefon {
180c540156cSNicolas Bonnefon LOG(logDEBUG) << "IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF for wd " << event->wd;
181c540156cSNicolas Bonnefon
182c540156cSNicolas Bonnefon // Retrieve the file
183b278d183SNicolas Bonnefon file = list->searchByFileOrSymlinkWd(
184b278d183SNicolas Bonnefon { event->wd }, { event->wd } );
185c540156cSNicolas Bonnefon }
186c540156cSNicolas Bonnefon else if ( event->mask & ( IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM ) )
187c540156cSNicolas Bonnefon {
18891f7c705SNicolas Bonnefon LOG(logDEBUG) << "IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM for wd " << event->wd
189c540156cSNicolas Bonnefon << " name: " << event->name;
190c540156cSNicolas Bonnefon
191c540156cSNicolas Bonnefon // Retrieve the file
192b278d183SNicolas Bonnefon file = list->searchByDirWdAndName( { event->wd }, event->name );
193c540156cSNicolas Bonnefon
194c540156cSNicolas Bonnefon if ( file )
195c540156cSNicolas Bonnefon {
196c540156cSNicolas Bonnefon LOG(logDEBUG) << "Dir change for watched file " << event->name;
19791f7c705SNicolas Bonnefon files_needing_readding->push_back( file );
198c540156cSNicolas Bonnefon }
199c540156cSNicolas Bonnefon }
200c540156cSNicolas Bonnefon else
201c540156cSNicolas Bonnefon {
202c540156cSNicolas Bonnefon LOG(logDEBUG) << "Unexpected event: " << event->mask << " wd " << event->wd;
203c540156cSNicolas Bonnefon }
204c540156cSNicolas Bonnefon
205c540156cSNicolas Bonnefon if ( file )
206c540156cSNicolas Bonnefon {
2073104b268SNicolas Bonnefon LOG(logDEBUG) << "Adding file: " << std::hex << file;
208b278d183SNicolas Bonnefon files_to_notify->push_back( file );
209c540156cSNicolas Bonnefon }
210c540156cSNicolas Bonnefon
211c540156cSNicolas Bonnefon return sizeof( struct inotify_event ) + event->len;
212c540156cSNicolas Bonnefon }
213b0345991SNicolas Bonnefon
interruptWait()214b0345991SNicolas Bonnefon void INotifyWatchTowerDriver::interruptWait()
215b0345991SNicolas Bonnefon {
216b0345991SNicolas Bonnefon char byte = 'X';
217b0345991SNicolas Bonnefon
218b0345991SNicolas Bonnefon (void) write( breaking_pipe_write_fd_, (void*) &byte, sizeof byte );
219b0345991SNicolas Bonnefon }
220