#!/usr/bin/perl
#
# This script generates logrotate configuration files for
# files in subdirectories of some "base" directory. Usefull on
# hosting servers, where logs of a specific site are placed under
# domain-named directory.
#
use File::Find;
use Cwd;
use strict;

### deine vars 
my $logdir="/var/log/chroot/apache"; # where dirs w/ logs liing.
my $logrotateconfigdir="/etc/logrotate.d/chrooted"; # where logrotate configs liing.
my $keeplogsize=2; # 'size' value for logrotate.conf (see man)
my $rotate=1;      # 'rotate' value for logrotate.conf (see man)
my $mode="0644";   # value for 'create' for logrotate.conf (see man)
my $owner="root";  # value for 'create' for logrotate.conf (see man)
my $group="wheel"; # value for 'create' for logrotate.conf (see man)
my $preexec="chattr -iR $logrotateconfigdir";	# This 'll be executed before starting 
						# work on $logrotateconfigdir.
my $postexec="chattr +iR $logrotateconfigdir";	# This 'll be executed after end of
						# work on $logrotateconfigdir.

### internal vars
my (%dirlist,$dir,@filelist,$file,$retcode);

### function(s) definition(s)

# gets a directory as a param, writes to global array a list of subdirs (except '.' & '..').
# non-recurcive (won't give sub-subdirs).
sub getdirlist
{ my (@contents,$object,$opendir,$cdir,$debug,$sub); $debug=undef; $sub="getdirlist";
  if ($_[0]) {$_=$_[0];} else {die "Err: no parameter in sub $sub.";}
  $debug && print "\$_: $_ \n";
  ( -d $_ ) || return 1; # we work only w/ dirs.
  $cdir=getcwd();  $debug && print "getcwd returns: $cdir \n"; # here we'll die if no rights to stat dir..
  # since we need to return only absolute paths some work on given paths:
  if (/^\//)
  {$debug && print "Path begins w/ / .\n";
   $opendir=$_;
  }
  else 
  {
   if ( "$_" eq "." )
   {$opendir=$cdir;}
   else
    {if (/^\.\./) # here we're just too lazy. =)
      {print "Err.. this script won't calculate absolute paths from relative begining with '..' .\n";
       print "Please specify a path as a <path from root, i.e. /var/log>,\n";
       print "or as <path from this dir, i.e ./log/apache >\n";
       return 1;
      }
     else 
     {$opendir=$cdir/$_;} 
    }
  }
  $debug && print "Doing opendir for:  $opendir \n";
  opendir(DIR,$opendir) || warn "Can't do opendir for '$opendir': ", $!;
  @contents = readdir(DIR);
  $debug && print "\nObjects found in `$opendir` :\n";
  $debug && print @contents;
  $debug && print "\nDirs found in '$opendir' :\n";
 foreach $object (@contents)
  {( -d "$opendir/$object" ) || next; # skip anything but dirs.
   ( -l "$opendir/$object" ) && next; # we won't play on symlinks even on symlinks to dirs.
   ( "$object" eq "." ) && next;
   ( "$object" eq ".." ) && next;
   $debug && print "\n$opendir/$object seem to be directory.\n";
   $dirlist{$object}="$opendir/$object";
  }
} # sub dodir

# returns a list of unpacked (not .gz) log files in a directory
# assumes that a directory path cannot be anything but absolute path.
# assumes that 'll run from find(), thus doesn't load $_.
sub getfilelist
{my ($cdir,$debug); $debug=0;
 $debug && print "$File::Find::dir :\t\t$_\n";
 #some checks on file name & type
 unless (/.gz$/) # we won't rotate already rotated.
  {unless ( -l "$File::Find::name" ) # we won't follow symlinks even to directories.
    {unless ( -d "$File::Find::name" ) # skip dirs.
      {unless ( "$_" eq "." )
        {unless ( "$_" eq ".." )
          {push (@filelist,"$File::Find::name");}
        }
      }
    }
  }
} # sub getfilelist


### start work
$retcode=getdirlist($logdir);
if ($retcode == 1) {print "Some error with path."; exit 1;}
system ($preexec)==0 || warn "Can't system $preexec .";
foreach $dir (keys %dirlist)
{@filelist=();
 find (\&getfilelist,$dirlist{$dir});
 open (CONFIG,">$logrotateconfigdir/$dir") || die "can't open $logrotateconfigdir/$dir for writing!";
 foreach $file (@filelist)
 {print CONFIG "$file {\n\t\tsize ",$keeplogsize,"M\n\t\trotate $rotate\n\t\tmissingok\n\t\tcreate $mode $owner $group\n\t\tpostrotate\n\t\t\t/etc/rc.d/init.d/apachectl restart\n\t\tendscript\n}\n\n";
 }
 close(CONFIG);
}
system ($postexec)==0 || warn "Can't system $postexec .";


