Skip to content

Instantly share code, notes, and snippets.

@dexterbt1
Last active August 10, 2022 15:15
Show Gist options
  • Save dexterbt1/3770410673480402ab0e34e312bd9343 to your computer and use it in GitHub Desktop.
Save dexterbt1/3770410673480402ab0e34e312bd9343 to your computer and use it in GitHub Desktop.
tail -f file with strftime filename path pattern + watch for new files
#!/usr/bin/perl -T
use strict;
use POSIX qw/strftime/;
my $DEBUG = defined($ENV{TAILFTS_DEBUG}) ? $ENV{TAILFTS_DEBUG} : 1;
my $LOGGER = sub { $DEBUG && print STDERR strftime("%Y-%m-%dT%H:%M:%S ", localtime(time())), @_; };
my $parent = $$;
my $USAGE = "USAGE: $0 <path/to/filename_pattern>\n";
my ($fname_pattern_raw) = @ARGV;
## quick and dirty taint checks
## --
$ENV{'PATH'} = '/bin:/usr/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
$fname_pattern_raw =~ /^([\/\.\w\%\\]+)$/g;
my $fname_pattern = $1;
my $fname;
if (length $fname_pattern <= 0) {
die $USAGE;
}
my $reset = 0;
my $tail_pid;
my $reset_check = sub {
my $fname_now = strftime($fname_pattern, localtime(time()));
if ($fname_now ne $fname) {
$fname = $fname_now;
$reset++;
kill 9, "-$tail_pid";
die "doreset\n";
}
alarm 1;
};
local $SIG{ALRM} = $reset_check;
local $SIG{INT} = sub { kill 9, "-$tail_pid"; exit 2; };
local $SIG{CHLD} = 'IGNORE';
while (1) {
$fname = strftime($fname_pattern, localtime(time()));
if (($reset<=0) && !(-f $fname)) {
die "ERROR: file not found: $fname\n";
}
$LOGGER->("$$: about to tail $fname\n");
eval {
my $lines = "10";
if ($reset <= 0) {
# first try
alarm 1; # prime mover
}
else {
# succeeding
$lines = "+1";
}
$tail_pid = fork();
if ($tail_pid == 0) {
# forked child
setpgrp(0, 0);
my $warned_wait = 0;
while (1) {
my $fname_now = strftime($fname_pattern, localtime(time()));
if ($fname_now ne $fname) {
$LOGGER->("$$: child-of($parent): time reset detected, giving up waiting for $fname ...\n");
exit 1;
}
if (-f $fname) {
last;
}
if (!$warned_wait) {
$LOGGER->("$$: child-of($parent): waiting for missing file: $fname ...\n");
$warned_wait = 1;
}
sleep 1;
}
if ($warned_wait) {
$LOGGER->("$$: child-of($parent): detected new file: $fname, starting tail\n");
}
exec("tail -n $lines -f $fname")
or die $!;
}
elsif (!defined $tail_pid) {
die "ERROR: fork error\n";
}
while (wait() != -1) {}
};
if ($@) {
die unless $@ eq "doreset\n"; # propagate unexpected errors
# did reset!
$LOGGER->("$$: resetting based on time change\n");
alarm 1; # prime mover
}
}
__END__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment