#!/usr/bin/perl -w

# copyright (C) 2001 neddix, stuttgart
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
#
#
# This program is part of the AWstats package for the e-smith Server and Gateway
#

use strict;
use Symbol;
use esmith::config;
use esmith::util;
use esmith::db;

sub fileinfo($);
sub write_runstat ($);
sub cleanup();

my $start_time=time();
my $httpdlog="/var/log/httpd";
my $awconfpath="/etc/e-smith/web/panels/manager/cgi-bin/.awstats";
my $awconf="$awconfpath/awstats.conf";
my $dirdata="/home/e-smith/files/users/admin/home/awstats";
my $lock="/var/lock/awstats";
my $logrotate_detected=0;
my $readlines=0;
my ($silent, $debug, $novoid);
(undef,undef,my $admin_uid)=getpwnam('admin');
(undef,undef,my $root_gid)=getgrnam('root');

my %conf;
tie %conf, 'esmith::config';

my $status=db_get_prop(\%conf, "AWStats", "status");
exit (0) if not defined $status || $status ne "enabled";
$debug=(db_get_prop(\%conf, "AWStats", "Debug") eq "yes");

# primitive command line options parsing
foreach (@ARGV) {
	$silent=1 if /-s/;
	$debug=1 if /-d/; # enter debug mode
	$novoid=1 if /-n/; # stop execution, if no new logs available
	if (/^-c/) {
		($awconf=$_) =~ s/^-c//;
	}
}

print "using configuration file $awconf\n" if $debug;


# check lock
if (-f "$lock") {
	open (LCK, "$lock");
	print "Another instance of AWStats is already running at pid=".<LCK>." \n" if $debug or not $silent;
	close (LCK);
	exit(1);
}

$SIG{TERM}='cleanup';
$SIG{KILL}='cleanup';
$SIG{INT}='cleanup';
END{cleanup()};

# set lock
open (LCK, ">$lock") or die "Could not create $lock\n";
print LCK "$$";
print "Set lock. pid=$$\n" if $debug;
close (LCK);

write_runstat ("Currently running. Please be patient. (pid=$$)");

my $log="$httpdlog/access_log";
my $log_1="$httpdlog/access_log.1";
my $cur_log; # filename of the current logfile
my $awpos="$httpdlog/awstats_pos";
my (@fstat_cur, @fstat_log, @fstat_log_1); # file status information of log files

die "Fatal error: Apache logfile $log not found\nIs Apache running?\n" if not -f $log;

# read file status information
@fstat_log=fileinfo("$log"); # current apache log file
@fstat_log_1=fileinfo("$log_1"); # logrotated apache log file

# read current log file information
if (-f "$awpos") {
	open (AWPOS,"$awpos") or die "Could not open file $awpos\n";
	chomp($_=<AWPOS>);
	@fstat_cur = split (":");
	close (AWPOS);
} else {
	 @fstat_cur=@fstat_log; # use current log file
	 $fstat_cur[2]=-1; # size
	 print "$awpos not found.\n" if $debug;
}

# check if information from $awpos is outdated:
# dev/inode pair must point to an Apache logfile
if ( ($fstat_cur[0]!=$fstat_log[0] || $fstat_cur[1]!=$fstat_log[1]) &&
	 ($fstat_cur[0]!=$fstat_log_1[0] || $fstat_cur[1]!=$fstat_log_1[1]) ) {
	 @fstat_cur=@fstat_log; # use current log file
	 $fstat_cur[2]=-1; # size
}

# find the filename and write size of logfile to current fstat
if ($fstat_cur[0]==$fstat_log[0] && $fstat_cur[1]==$fstat_log[1]) {
	$cur_log=$log;	# use current Apache log
	$fstat_cur[2]=$fstat_log[2];
} elsif ($fstat_cur[0]==$fstat_log_1[0] && $fstat_cur[1]==$fstat_log_1[1]) {
	$cur_log=$log_1; # use logrotated Apache log
	$fstat_cur[2]=$fstat_log_1[2];
	$logrotate_detected=1; # Flag: remove $awpos at exit
} else {
	die "Fatal error: No Apache logfile found.\nIs Apache running?\n";
}

print "fstat=$fstat_cur[0]:$fstat_cur[1]:$fstat_cur[2]:$fstat_cur[3]\n" if $debug;
print "Current log filename: $cur_log\n" if $debug;
print "logrotated: " . ($logrotate_detected==1 ? "yes\n" : "no\n") if $debug;

mkdir ("$dirdata");

# check if anybody played with dirdata permissions, i.e. e-smith upgrade
{
	(undef,undef,my $mode,undef,my $uid,my $gid)=stat($dirdata);
	if (($mode &= 0777) != 0770 || $uid != $admin_uid || $gid != $root_gid) {
		print "setting recursivly correct permissions for $dirdata\n" if $debug;
		my @list=<$dirdata $dirdata/* $dirdata/*/*>;
		chown ($admin_uid, $root_gid, @list);
		chmod (0770, $root_gid, @list);
	}
}

if ($fstat_cur[2]==$fstat_cur[3] && $novoid) {
	print "Nix zu tun.\n" if $debug;
	exit(0);
}

my $key;
my $value;
my %Domains;

# split main log into log for each domain
mkdir ("$httpdlog/awstats");
open (LOG, "$cur_log") or die "Could not open $cur_log\n";
seek (LOG, $fstat_cur[3], 0) or die "seek failed\n";
while (<LOG>) {
	my $line=$_;
	$line=~s/(^.*?) (.*)/$2/;
	my $key=$1;
	if (not defined $Domains{$key}) {
		$Domains{$key}=gensym();
		open ($Domains{$key}, ">$httpdlog/awstats/$key") or die "Could not open $httpdlog/awstats/$key\n";
	}
	# write in Apaches combined log format
	print {$Domains{$key}} $line;
	$readlines++;
}
print "  startoffset=$fstat_cur[3]\n" if $debug;
$fstat_cur[3]=tell(LOG); # save the file offset
print "  endoffset=$fstat_cur[3]\n" if $debug;
print "  read $readlines lines\n" if $debug;
close (LOG);

# close log files for virt domains
while (($key,$value) = each %Domains) {

	close ($Domains{$key});
}

# generate domain specific conf files for both
# newly scanned and historical ones
# must be done every run, since they could be deleted
# or default config might have been modified
my %ConfDomains=%Domains;
opendir (DIR, "$dirdata");
while (defined (my $d=readdir(DIR))) {
	next if $d =~ /^\.\.?$/;
	$ConfDomains{$d}=gensym() if not defined $ConfDomains{$d};
}
closedir (DIR);
while (($key,$value) = each %ConfDomains) {
	# read default config file (built by using e-smith template mechanism)
	open ($ConfDomains{$key}, ">$awconfpath/awstats.$key.conf") or die "Could not open $awconfpath/awstats.$key.conf\n";
	# write domain specific config file
	open (CONF, "$awconf") or die "Could not open $awconf\n";
	while (<CONF>) {
		if (/^DirData=/) {
			mkdir ("$dirdata/$key"); # make dir for AWStats historical data
			chown ($admin_uid,$root_gid, "$dirdata/$key");
			chmod (0770,"$dirdata/$key");
			$_="DirData=\"$dirdata/$key\"\n"; # path to history dir
		}
		if (/^SiteDomain=/) {
			$_="SiteDomain=\"$key\"";
		}
		$_ =~ s/LogFile="\/var\/log\/httpd\/access_log"/LogFile="-"/;
		print {$ConfDomains{$key}} $_;
	}
	close (CONF);
	close ($ConfDomains{$key});
}

# launch AWStats for each domain, which has new logs
while (($key,$value) = each %Domains) {
	my $ret;
	my $cmd="/etc/e-smith/web/panels/manager/cgi-bin/.awstats/awstats.pl -config=$key < $httpdlog/awstats/$key > /dev/null";
	print "Launching AWStats with logfile $httpdlog/awstats/$key (len="
		.(-s "$httpdlog/awstats/$key") . " bytes)\n" if $debug;
	$ret=system ("$cmd");
	print "Command was: $cmd\n" if $ret!=0;
}

# save the status of the Apache log for the next run
open (AWPOS,">$awpos") or die "Could not open file $awpos for writing\n";
print AWPOS $fstat_cur[0].":".$fstat_cur[1].":".$fstat_cur[2].":".$fstat_cur[3]."\n";
print AWPOS "# THIS FILE IS NEEDED BY AWSTATS. DO NOT DELETE!\n";
close (AWPOS);
print "$awpos: $fstat_cur[0]:$fstat_cur[1]:$fstat_cur[2]:$fstat_cur[3]\n" if $debug;

exit (0);


# get dev, inode and size information of a file
sub fileinfo($) {
	my ($file)=@_;
	return (0,0,0,0) if not -f $file;
	my @info; # 0-> device, 1-> inode, 2->size
	($info[0],$info[1],undef,undef,undef,undef,undef,$info[2])=stat($file);;
	$info[3]="0"; # default file pos offset
	print "$file: $info[0]:$info[1]:$info[2]:$info[3]\n" if $debug;
	return @info;
}

sub write_runstat ($) {
	open (ST, ">$httpdlog/awstats_lr") or die "Could not open file $httpdlog/awstats_lr for writing\n";
	print ST $_[0];
	close (ST);
}

# invoked at exit, SIGTERM, SIGKILL and SIGINT
sub cleanup () {
	my ($xtime, $min, $hour, $day, $month, $year);
	(undef, $min, $hour, $day, $month, $year)=localtime($start_time);
	$xtime=(time()-$start_time);
	print "Program execution time: $xtime sec\n"  if $debug;
	print "Cleanup.\n" if $debug;
	if (not $debug) {
		while (($key,$value) = each %Domains) {
			unlink ("$httpdlog/awstats/$key");
		}
		rmdir ("$httpdlog/awstats");
	}
	if ($logrotate_detected==1) {
    	unlink ("$awpos");
		print "Logfile rotate detected. $awpos removed. Next run will process the new logfile\n" if $debug;
	}
	write_runstat (sprintf ("Updated on %02d/%02d/%d at %02d:%02d in %d secs. %d new logs processed.",
					$month+1,$day,$year+1900,$hour,$min,$xtime, $readlines));
	if (-f "$lock") {
		open (LCK, "$lock");
		my $pid=<LCK>;
		close (LCK);
		unlink ("$lock") if $$ == "$pid" or "$pid" eq "";
	}
	# remove temporary conf file
	if ($awconf =~ /\.[0-9]*$/) {
		unlink ("$awconf");
	}
	exit (0);
}
