#!perl
use
5.008008;
our
$VERSION
=
$App::watcher::VERSION
;
my
@dir
;
my
@exclude_dir
;
my
$signal
=$^O eq
'MSWin32'
?
'KILL'
:
'TERM'
;
GetOptions(
'dir=s@'
=> \
@dir
,
'exclude=s@'
=> \
@exclude_dir
,
'signal=s'
=> \
$signal
,
'send_only'
=> \
my
$send_only
,
'h|help'
=> \
my
$help
,
'v|version'
=> \
my
$version
,
'filter=s@'
=> \
my
@filters
,
) or pod2usage;
$version
and
do
{
print
"watcher: $VERSION\n"
;
exit
0 };
pod2usage(1)
if
$help
;
pod2usage(1)
unless
@ARGV
;
@dir
= (
'.'
)
unless
@dir
;
$_
=
qr/$_/
for
@filters
;
push
@filters
,
qr!^\.[^\.]|[/\\][\._][^\.]|\.bak$|~$|_flymake\.(?:p[lm]|t)!
unless
@filters
;
if
(
@exclude_dir
) {
@exclude_dir
=
map
{ File::Spec->abs2rel(
$_
) }
@exclude_dir
;
}
sub
info {
my
(
$sec
,
$min
,
$hour
,
$mday
,
$mon
,
$year
,
$wday
,
$yday
,
$isdst
) =
localtime
(
time
);
my
$time
=
sprintf
(
"%04d-%02d-%02dT%02d:%02d:%02d"
,
$year
+ 1900,
$mon
+ 1,
$mday
,
$hour
,
$min
,
$sec
);
print
"[$time] "
,
join
(
' '
,
@_
),
"\n"
;
}
my
$pid
;
sub
fork_and_start {
undef
$pid
;
$pid
=
fork
;
die
"Can't fork: $!"
unless
defined
$pid
;
if
(
$pid
== 0 ) {
$SIG
{INT} =
$SIG
{HUP} =
$SIG
{TERM} =
'DEFAULT'
;
exec
@ARGV
;
die
"Cannot exec: @ARGV"
;
}
else
{
info(
"Forked process: @ARGV"
);
}
}
sub
kill_pid {
$pid
or
return
;
info(
"Killing the existing process by $signal (pid:$pid)"
);
kill
$signal
=>
$pid
;
waitpid
(
$pid
, 0 );
}
sub
send_signal {
info(
"Sending $signal to the existing process (pid:$pid)"
);
kill
$signal
=>
$pid
;
}
info(
"watching: @dir"
);
fork_and_start();
exit
(0)
unless
$pid
;
for
my
$sig
(
qw(TERM HUP INT)
) {
$SIG
{
$sig
} =
sub
{
info(
"SIG$sig received"
);
finalize();
};
}
my
$watcher
= Filesys::Notify::Simple->new(\
@dir
);
while
(1) {
my
@restart
;
$watcher
->
wait
(
sub
{
my
@events
=
@_
;
@events
=
grep
{ valid_file(
$_
) }
map
{
$_
->{path} }
@events
;
@restart
=
@events
;
});
next
unless
@restart
;
info(
"-- $_"
)
for
@restart
;
if
(
$send_only
) {
send_signal();
}
else
{
kill_pid();
info(
"Successfully killed! Restarting the new process."
);
fork_and_start();
unless
(
$pid
) {
exit
(0);
}
}
}
sub
finalize {
my
$self
=
shift
;
if
(
$pid
) {
info(
"Terminate process: $pid"
);
kill
'TERM'
=>
$pid
;
waitpid
(
$pid
, 0 );
}
exit
0;
}
sub
valid_file {
my
(
$file
) =
@_
;
my
$rel
= File::Spec->abs2rel(
$file
);
return
if
any {
$rel
=~
$_
}
@filters
;
return
if
any {
index
(
$rel
,
$_
) == 0 }
@exclude_dir
;
return
1;
}