/*
* Copyright (C) 2015 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* glogg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with glogg. If not, see .
*/
#include "inotifywatchtowerdriver.h"
#include
#include
#include
#include
#include "log.h"
#include "watchtowerlist.h"
INotifyWatchTowerDriver::INotifyWatchTowerDriver() : inotify_fd_( inotify_init() )
{
int pipefd[2];
pipe2( pipefd, O_NONBLOCK );
breaking_pipe_read_fd_ = pipefd[0];
breaking_pipe_write_fd_ = pipefd[1];
}
INotifyWatchTowerDriver::~INotifyWatchTowerDriver()
{
close( breaking_pipe_read_fd_ );
close( breaking_pipe_write_fd_ );
}
INotifyWatchTowerDriver::FileId INotifyWatchTowerDriver::addFile(
const std::string& file_name )
{
// Add a watch for the inode
int wd = inotify_add_watch( inotify_fd_, file_name.c_str(),
IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF );
LOG(logDEBUG) << "INotifyWatchTower::addFile new inotify wd " << wd;
return { wd };
}
INotifyWatchTowerDriver::SymlinkId INotifyWatchTowerDriver::addSymlink(
const std::string& file_name )
{
int symlink_wd = inotify_add_watch( inotify_fd_, file_name.c_str(),
IN_DONT_FOLLOW | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF );
LOG(logDEBUG) << "INotifyWatchTower::addFile new inotify symlink_wd " << symlink_wd;
// (not sure a symlink can be modified but you never know)
return { symlink_wd };
}
INotifyWatchTowerDriver::DirId INotifyWatchTowerDriver::addDir(
const std::string& file_name )
{
int dir_wd = inotify_add_watch( inotify_fd_, file_name.c_str(),
IN_CREATE | IN_MOVE | IN_ONLYDIR );
LOG(logDEBUG) << "INotifyWatchTower::addFile dir " << file_name
<< " watched wd " << dir_wd;
return { dir_wd };
}
void INotifyWatchTowerDriver::removeFile(
const INotifyWatchTowerDriver::FileId& file_id )
{
/*
LOG(logDEBUG) << "INotifyWatchTower::removeNotification removing inotify wd "
<< file->file_wd_ << " symlink_wd " << file->symlink_wd_;
*/
if ( file_id.wd_ >= 0 )
inotify_rm_watch( inotify_fd_, file_id.wd_ );
}
void INotifyWatchTowerDriver::removeSymlink( const SymlinkId& symlink_id )
{
if ( symlink_id.wd_ >= 0 )
inotify_rm_watch( inotify_fd_, symlink_id.wd_ );
}
void INotifyWatchTowerDriver::removeDir( const DirId& dir_id )
{
LOG(logDEBUG) << "INotifyWatchTower::removeDir removing inotify wd " << dir_id.wd_;
if ( dir_id.wd_ >= 0 )
inotify_rm_watch( inotify_fd_, dir_id.wd_ );
}
static constexpr size_t INOTIFY_BUFFER_SIZE = 4096;
std::vector
INotifyWatchTowerDriver::waitAndProcessEvents(
INotifyObservedFileList* list,
std::unique_lock* list_lock,
std::vector* files_needing_readding,
int timeout_ms )
{
std::vector files_to_notify;
struct pollfd fds[2];
fds[0].fd = inotify_fd_;
fds[0].events = POLLIN;
fds[0].revents = 0;
fds[1].fd = breaking_pipe_read_fd_;
fds[1].events = POLLIN;
fds[1].revents = 0;
list_lock->unlock();
int poll_ret = poll( fds, 2, timeout_ms ? timeout_ms : -1 );
list_lock->lock();
if ( poll_ret > 0 )
{
if ( fds[0].revents & POLLIN )
{
LOG(logDEBUG) << "Pollin for inotify";
char buffer[ INOTIFY_BUFFER_SIZE ]
__attribute__ ((aligned(__alignof__(struct inotify_event))));
ssize_t nb = read( inotify_fd_, buffer, sizeof( buffer ) );
if ( nb > 0 )
{
ssize_t offset = 0;
while ( offset < nb ) {
const inotify_event* event =
reinterpret_cast( buffer + offset );
offset += processINotifyEvent( event, list,
&files_to_notify, files_needing_readding );
}
}
else
{
LOG(logWARNING) << "Error reading from inotify " << errno;
}
}
if ( fds[1].revents & POLLIN )
{
uint8_t byte;
read( breaking_pipe_read_fd_, &byte, sizeof byte );
}
}
return files_to_notify;
}
// Treats the passed event and returns the number of bytes used
size_t INotifyWatchTowerDriver::processINotifyEvent(
const struct inotify_event* event,
INotifyObservedFileList* list,
std::vector* files_to_notify,
std::vector* files_needing_readding )
{
LOG(logDEBUG) << "Event received: " << std::hex << event->mask;
INotifyObservedFile* file = nullptr;
if ( event->mask & ( IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF ) )
{
LOG(logDEBUG) << "IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF for wd " << event->wd;
// Retrieve the file
file = list->searchByFileOrSymlinkWd(
{ event->wd }, { event->wd } );
}
else if ( event->mask & ( IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM ) )
{
LOG(logDEBUG) << "IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM for wd " << event->wd
<< " name: " << event->name;
// Retrieve the file
file = list->searchByDirWdAndName( { event->wd }, event->name );
if ( file )
{
LOG(logDEBUG) << "Dir change for watched file " << event->name;
files_needing_readding->push_back( file );
}
}
else
{
LOG(logDEBUG) << "Unexpected event: " << event->mask << " wd " << event->wd;
}
if ( file )
{
LOG(logDEBUG) << "Adding file: " << std::hex << file;
files_to_notify->push_back( file );
}
return sizeof( struct inotify_event ) + event->len;
}
void INotifyWatchTowerDriver::interruptWait()
{
char byte = 'X';
(void) write( breaking_pipe_write_fd_, (void*) &byte, sizeof byte );
}