#! /usr/bin/perl
#
# file: apache-log-rotate.pl
# purpose: rotates all apapche logfiles
# details:
#       - reads httpd.conf and all conf files included (recursive search).
#	- finds all CustomLog and ErrorLog enties.
#	- rotates the logs.
#	- restarts apapches.
#	- dates and gzips the log files.
# dependancies to note:
#	The following may need to be customized for each machine.
#	- uses /bin/date, /usr/bin/gzip
#	- uses /usr/sbin/apachectl
#	The following use global variables:
#       - assumes the server root is "/var/www/" ( $server_root )
#	- assumes the main config file is "/var/www/conf/httpd.conf"
#	  ( $conf_file )
# author: chad clark < frink _at_ thepurplebuffalo _dot_ net >
# date: 04 may 2002
#
# Reasons to use this script:
#	- recursively includes config files.
#	- dates and gzips the log files.
# Reasons not to use this script:
#	- restarts apache so you _may_ lose sessions. (eg coldfusion)
# How I use this script:
#	- in a weekly cron.
# Who can use this script:
#	- anyone who wants to in any way they see fit.
#
# NOTE: This script works for me.  It may work for you if you choose 
#	to use it.  If it does not work for you I will not accept any
#	responsibility for whatever happens.  Read the code and test
#	it under the setup you intend to use it before using it on
#	important data.
#	( don't you hate a society where disclaimers are needed? )
#
# updates:

use strict;

# top level config file.
my $conf_file = "/var/www/conf/httpd.conf";
# server root used for non-absolute log file paths.
my $server_root = "/var/www/";

my $date = `/bin/date +".%Y-%m-%d"`;
chomp $date;

## get a list of all config files to be examined.
my @conf_files = get_conf_filenames($conf_file);

print "\nConfig Files:\n";
map { chomp $_ } @conf_files;
map { print $_ . "\n" } @conf_files;

## collect the log files from each config file.
my @log_files = ();
my $file;
foreach $file (@conf_files) {
	my @local_logs = logs_from_file($file);
	@log_files = (@log_files, @local_logs);
}

# sort and get a unique list of files (thanks to perl FAQ Q 5.4)
my %tmp_hash;
@tmp_hash{@log_files} = ();
@log_files = sort keys %tmp_hash;  

# make all paths absolute.
foreach $file (@log_files) {
	if ( $file =~ m/^[^\/]/ ) {
		$file = $server_root . $file;
	}
}

print "\nLog Files:\n";
map { chomp $_ } @log_files;
map { print $_ . "\n" } @log_files;

## rotate (ie date) all log files.
print "Rotating log files...\n";
foreach $file (@log_files) {
	if ( -e $file ) {
		rename ($file, $file . $date) 
		  or die ("Cant rename $file to $file" . $date);
	}
}

## HUP apache.
print "Restarting apache...\n";
my $hup = "/usr/sbin/apachectl restart";
$hup = `$hup`;

## gzip all dated logfiles.
print "gzipping the log files...\n";
foreach $file (@log_files) {
	chomp $file;
	if ( -e $file . $date ) {
		my $gz = "/usr/bin/gzip -f " . $file . $date;
		print $gz . "\n";
		$gz = `$gz`;
	}
}

exit 0;


## SUBROUTINES ##########################################

# get_conf_filenames($conf_file) ########################
# purpose: gets a list of all apache config files included
# 	by $file.
#
sub get_conf_filenames($conf_file) {
	my @filenames = ($_[0]);

	#print "conf file: $_[0]\n";

	open (INP, $_[0]) or die "Cant open $_[0]";
	my @lines = <INP>;
	close (INP);

	map ( { s/^\s*// } @lines);		# intro whitespace
	@lines = grep ( /^Include/ , @lines);	# Include lines

	map ( { s/^Include\s*// } @lines);
	#print @lines;

	my $count = @lines;
	if ($count != 0) {
		my $line;
		foreach $line (@lines) {
			#print "\ncalling with arg: $line";
			my @localfiles = get_conf_filenames($line);
			@filenames = (@filenames, @localfiles);
			#print "\n" . @filenames . "\n";
		}
	}

	#print @lines;

	#print "\n";
	#print @filenames;
	#print "\n";
	return (@filenames);
}



# logs_from_files() #####################################
# purpose: gets alist of all ErrorLog and CustomLog files
# 	used by $file;
#
sub logs_from_file($file) {
	chomp $_[0];

	#print "logs_from_file: $_[0]\n";

	open (INP, $_[0]);
	my @conf_lines = <INP>;
	close (INP);

	my @conf_lines = grep ( /_log/ , @conf_lines);  # start w/ log file lines.
                                                	# and remove:
	my $filename;
	foreach $filename (@conf_lines) {
        	$filename =~ s/^\s*// ;                 #   begining whitespace lines.
	}
	@conf_lines = grep ( !/Module/ , @conf_lines);  #   load/add module lines.
	@conf_lines = grep ( !/#/ , @conf_lines);       #   commented out lines.

	my @error_files = grep ( /^ErrorLog/ , @conf_lines);
	map ( { s/^ErrorLog\s*// } @error_files);       #   "^ErrorLog "

	my @custom_files = grep ( /^CustomLog/ , @conf_lines);
	map ( { s/^CustomLog\s*// } @custom_files);     #   "^CustomLog "
	map ( { s/ .*$// } @custom_files);              #   custom log format

	#print "\n\nx\n\n"; print @error_files;
	#print "\n\ny\n\n"; print @custom_files;

	my @logfiles = ( @error_files , @custom_files ) ;

	#print "\n\nz\n\n"; print @logfiles;

	map ( { chomp $_ } @logfiles );

	return (@logfiles);
}

