#!/usr/bin/perl
# ****************************************************************
# file: cronolog
# date: 2007-08-24 10:35:27
# author: Marko Schulz - <info@tuxnet24.de>
#
# description: cronolog is a simple filter program that reads log
# file entries from standard input and writes each entry to the
# output file specified by a filename template and the current
# date and time.
# ****************************************************************
use strict;
# define the rotation time (HHMM)...
my $rotation_time_hhmm="00:00";
# load package for command line arguments (GetOptions)...
use Getopt::Long;
# load package for file handling (dirname)...
use File::Basename;
# define variables...
use vars qw( $opt_V $opt_p $opt_s $opt_t $opt_h $file );
# set alarm handler and trigger initial alarm
$SIG{ALRM} = \&sig_alarm;
alarm(1);
# define command line arguments...
Getopt::Long::Configure('bundling');
GetOptions
( "V" => \$opt_V, "version" => \$opt_V,
"p=s" => \$opt_p, "period=s" => \$opt_p,
"t=i" => \$opt_t, "tzone=i" => \$opt_t,
"s=s" => \$opt_s, "symlink=s" => \$opt_s,
"h" => \$opt_h, "help" => \$opt_h );
# default time shift is 0 if opt_t is not defined...
$opt_t=0 if ( !$opt_t );
# ****************************************************************
# check cammandline arguments...
# display help or usage...
if ( $opt_h ) {
print "<STDIN> | cronolog --symlink|-s /path/to/symlink [ --tzone|-t <time shift> --period|";
print "-p 1day --help|-h --version|-V ] /path/to/logfile-\%Y-\%m-\%d.log\n\n";
print "\t--symlink|-s /path/to/symlink (required)\n";
print "\t--tzone|-t int (-12..+12) (default 0)\n";
print "\t--period|-p rotation period (ignored, we rotate at 00:00)\n";
print "\t--help|-h display this screen\n";
print "\t--version|-V display version\n\n";
exit 0;
}
# print out a cronolog version...
if ( $opt_V ) {
print STDERR "cronolog version 1.7.0\n";
exit 0;
}
# error message if no logfile defined...
if ( !$ARGV[-1] ) {
print "cronolog: No logfile defined.\n";
exit 1;
}
# print error message if logfile directory is not writable...
if ( ! -w dirname( $ARGV[-1] ) ) {
print "cronolog: Can't create logfile -".$opt_s."- because the directory is not writable.\n";
exit 1;
}
# remove leading character...
$opt_s =~ s/^=//g;
# error message if no symlink defined...
if ( !$opt_s ) {
print "cronolog: No symlink defined.\n";
exit 1;
}
# print error message if symlink directory is not writable...
if ( ! -w dirname( $opt_s ) ) {
print "cronolog: Can't create symlink -".$opt_s."- because the directory is not writable.\n";
exit 1;
}
# ****************************************************************
# program action...
# let the signal handler run at least once
sleep(1);
# loop until STDIN is closed...
while (<STDIN>) {
# get current filename from template...
$file=get_filename($ARGV[-1]);
# flush buffer...
$|=1;
# write data from STDIN...
open (DAT, ">>$file") or die( "Can't open -".$file."-: ".$! );
print DAT $_;
close(DAT);
}
# ****************************************************************
# return logfile name from logfile template...
sub get_filename {
my $filename=shift;
# get date as array reference( YYYY, MM, DD, HH, MM, SS )...
my $ref_date = date( $opt_t );
# replace placeholder ( %Y, %m, %d, %H, %M, %S => strftime )...
$filename =~ s!\%Y!$ref_date->[0]!g;
$filename =~ s!\%m!$ref_date->[1]!g;
$filename =~ s!\%d!$ref_date->[2]!g;
$filename =~ s!\%H!$ref_date->[3]!g;
$filename =~ s!\%M!$ref_date->[4]!g;
$filename =~ s!\%S!$ref_date->[5]!g;
return $filename;
}
# ****************************************************************
# signal handler, invoked on SIG_ALRM...
sub sig_alarm {
# get target file of the symlink...
my $symlink=$opt_s;
my $target = readlink( $symlink );
# get date as array reference( YYYY, MM, DD, HH, MM, SS )...
my $ref_date = date( $opt_t );
# refresh filename to use
$file=get_filename($ARGV[-1]);
# create new file (only) if it does not exist...
if ( ! -f $file || $ref_date->[3].":".$ref_date->[4] eq $rotation_time_hhmm ) {
# check if symlink with name of log file exists that would prevent logfile creation
if ( -l $file) {
# remove symlink only if symlink or file exists...
unlink( "$file" ) or die( "Can't remove symlink -".$file."-: ".$! );
}
# open file in append mode (may have been created from within while loop!)
open (DAT, ">>$file") or die( "Can't open file -".$file."-: ".$! );
close(DAT);
}
# remove symlink if target file and current logfile are not equal or rotation_time is reached...
if ( $target ne $file || $ref_date->[3].":".$ref_date->[4] eq $rotation_time_hhmm ) {
# remove symlink only if symlink or file exists...
unlink( "$symlink" ) or die( "Can't remove symlink -".$symlink."-: ".$! ) if ( -l $symlink || -f $symlink );
# create symlink to current logfile if no symlink exists...
symlink( "$file", "$symlink" ) or die( "Can't create symlink -".$symlink."- to -".$file."-: ".$! ) if ( ! -l $symlink );
}
# trigger new alarm to occur when next minute begins ...
my $ref_date = date( $opt_t );
alarm( 60-$ref_date->[5] );
}
# ****************************************************************
# return date as array ( %Y, %m, %d, %H, %M, %S => strftime )...
sub date {
my $tzone=shift;
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time+($tzone*3600));
$year = 1900+$year;
$mon = $mon+1;
$sec = sprintf( "%02d", $sec );
$min = sprintf( "%02d", $min );
$hour = sprintf( "%02d", $hour );
$mon = sprintf( "%02d", $mon );
$mday = sprintf( "%02d", $mday );
my @date=( $year, $mon, $mday, $hour, $min, $sec );
return \@date;
}
# ****************************************************************
# end of this script...