$App::CSE::Command::Watch::VERSION = '0.012';
use Moose;
extends qw/App::CSE::Command/;
my $LOGGER = Log::Log4perl->get_logger();
sub execute{
my ($self) = @_;
my $colorizer = $self->cse()->colorizer();
my $colored = sub{ $colorizer->colored(@_);};
my $cse = $self->cse();
# Check the index.
# Re-index if nothing is there.
my $check = App::CSE::Command::Check->new({ cse => $cse });
if( $check->execute() ){
$LOGGER->info(&$colored("Rebuilding the index..", 'green bold'));
my $index_cmd = App::CSE::Command::Index->new( { cse => $cse });
if( $index_cmd->execute() ){
$LOGGER->error(&$colored("Building index failed", 'red'));
return 1;
}
}
if( my $previous_pid = $cse->index_meta()->{'watcher.pid'} ){
# A previous pid should be a number.
( $previous_pid ) = ( $previous_pid =~ /(\d+)/ );
if( kill(0, $previous_pid ) ){
$LOGGER->error(&$colored("Previous watcher (PID=".$previous_pid.") is still running. Try cse unwatch first",
"red bold"));
return 1;
}
}
my $direct_child = fork();
confess("Cannot fork() a direct child: $!") unless defined $direct_child;
if( $direct_child ){
waitpid($direct_child , 0 );
return 0;
}
my $pid = fork();
confess("Cannot fork() a worker child: $!") unless defined $pid;
if( $pid ){
$cse->index_meta()->{'watcher.pid'} = $pid;
$cse->index_meta()->{'watcher.started'} = DateTime->now()->iso8601();
$cse->save_index_meta();
$LOGGER->info(&$colored("PID=$pid : Watching for changes in ".$self->dir_index().", updating ".$cse->index_dir(), "green bold"));
exit(0);
}
my $deamon_log = q|log4perl.rootLogger = INFO, SYSLOG
log4perl.appender.SYSLOG = Log::Dispatch::Syslog
log4perl.appender.SYSLOG.min_level = debug
log4perl.appender.SYSLOG.ident = cse V|.$cse->version().q|[|.$$.q|]
log4perl.appender.SYSLOG.facility = daemon
log4perl.appender.SYSLOG.layout = Log::Log4perl::Layout::SimpleLayout
|;
Log::Log4perl::init(\$deamon_log);
$LOGGER->info("Watching for changes in ".$self->dir_index().", updating ".$cse->index_dir());
my $lock = 0;
my $should_exit = 0;
$SIG{TERM} = $SIG{INT} = sub{
$LOGGER->info("Caught INT or TERM signal. Will exit");
$should_exit = 1;
unless( $lock ){
exit(0);
}
};
my $watcher = Filesys::Notify::Simple->new([ $self->dir_index()->absolute()->stringify() ]);
while( !$should_exit ){
$watcher->wait(sub {
my @events = @_;
# Lock exiting before we do anything meaty
$lock = 1;
eval{
foreach my $event ( @events ) {
my $file_name = $event->{path};
# file_path here is absolute.
# $self->dir_index() can be relative
if ( $self->dir_index()->is_relative() ) {
# We should remove the absolute prefix from the file path
my $abs_prefix = $self->dir_index->absolute()->stringify();
$file_name =~ s/^$abs_prefix/\./ ;
}
# Unreadable files are invalid. This will help
# not adding deleted files.
my $is_hidden = 0;
my $is_gone = 0;
$cse->is_file_valid( $file_name , { on_hidden => sub{ $is_hidden = 1 ; return 0; },
on_unreadable => sub{ $is_gone = 1 ; return 0; },
on_skip => sub{ $is_gone = 1 ; return 0; } # Consider skipped files as gone.
} );
if( $is_hidden ){
# We dont do anything about hidden files.
next;
}
# Build an indexer just for this event. This is to avoid complex
# committing logic.
my $searcher = Lucy::Search::IndexSearcher->new( index => $cse->index_dir().'' );
my $indexer = Lucy::Index::Indexer->new( schema => $searcher->get_schema(),
index => $cse->index_dir().'' );
# Delete the file anyway.
$LOGGER->debug("Deleting $file_name from index");
$indexer->delete_by_term( field => 'path.raw',
term => $file_name );
# If the file is gone, that is it. We need to commit and go to the next iteration.
if( $is_gone ){
$indexer->commit();
next;
}
my $mime_type = $cse->valid_mime_type($file_name);
unless( $mime_type ){
$indexer->commit();
next;
}
my $file_class = App::CSE::File->class_for_mime($mime_type, $file_name.'');
unless( $file_class ){
$indexer->commit();
next;
}
## Build a file instance.
my $file = $file_class->new({cse => $cse,
mime_type => $mime_type,
file_path => $file_name.'' })->effective_object();
my $content = $file->content();
$LOGGER->debug("Adding $file_name to index as ".$file->mime_type());
$indexer->add_doc({
path => $file->file_path(),
'path.raw' => $file->file_path(),
dir => $file->dir(),
mime => $file->mime_type(),
mtime => $file->mtime->iso8601(),
$content ? ( content => $content ) : ()
});
$indexer->commit();
} # End of event loop
};
if ( my $err = $@ ) {
$LOGGER->error("ERROR reacting to event: $err");
}
# Unlock exiting
$lock = 0;
if ( $should_exit ) {
exit(0);
}
});
sleep(10);
} # End of while !$should_exit
$LOGGER->warn("Terminating myself. Should not occur");
exit(0);
}
__PACKAGE__->meta->make_immutable();