###
###  Copyright 2000-2007 University of Illinois Board of Trustees
###  All rights reserved. 
###
###  PSGConf::Control::logadm - psgconf control class for controlling log rotation/pruning for Solaris
###
###  Campus Information Technologies and Educational Services
###  University of Illinois at Urbana-Champaign
###


package PSGConf::Control::logadm;

use PSGConf::Action::GenerateFile::Literal;

use PSGConf::Data::Boolean;
use PSGConf::Data::Hash;
use PSGConf::Data::Integer;
use PSGConf::Data::List;
use PSGConf::Data::String;

###############################################################################
### policies
###############################################################################

sub _policy_merge_timestamps 
{
	my ($self, $psgconf) = @_;
	my ($line, $line2, $obj, $log, @data);
	local *FP;

	return
		if ($psgconf->data_obj('logadm_enable')->equals('false'));

	if (!open (FP, $psgconf->data_obj('logadm_conf_file')->get())) {
		warn "\n\t!!! can't open existing logadm conf file (%s)\n",
			$psgconf->data_obj('logadm_conf_file')->get()
			if ( $psgconf->{verbose} );
		return;
	}
	@data = <FP>;
	close FP;

	foreach $line ( @data ) {

		### Skip this line if we do not have any timestamp info...
		next if ( $line !~ / -P / );

		chomp $line;

		### Find the log file we are working with
		$log = $line;
		$log =~ s/ .*$//;

		### Now get the time stamp itself (w/o the single quotes).
		$line =~ s/^.* -P '//;
		$line =~ s/' .*$//;

		### Validate that we have a good timestamp.
		next if ( $line != m/
				^
				(\S\S\S)			# Day of the week
				\s+
				(\S\S\S)			# Month
				\s+
				(\d?\d)			# Day
				\s+
				(\d\d:\d\d:\d\d)	# Time (HH:MM:SS)
				\s+
				(\d\d\d\d)		# Year
				$
			/);

		### Now put the timestamp back into our directives.
		### See if we already have a line in logadm_files
		$line2 = $psgconf->data_obj('logadm_files')->find("^$log");

		if ( defined $line2 ) {
			### If we already have a timestamp in the config, just go on.
			next if ( $line2 =~ / -P / );

			### Search through the arguments to find out where
			### to put this timestamp.
			my (@a) = split(/\s/, $line2);
			for (my $i=1; $i < scalar @a; $i++) {
				if ( $a[$i] =~ /^-[ACE]$/ ) {
					$i++;

				### Loop through the -M 'some cmd' arguments
				} elsif ( $a[$i] eq '-M' ) {
					do { $i++ } while ( $a[$i] !~ /\'$/ );

				} elsif ( $a[$i] ne '-N' ) {

					$obj = $line2;
					$obj =~ s/ $a[$i]/ -P '$line' $a[$i]/;
					$psgconf->data_obj('logadm_files')->replace(
						"^$line2",
						$obj
					);

					### Now exit out of this inner for loop...
					$i = scalar @a;
				}
			}

		### Since we do not have an entry in logadm_files,
		### store the value for use later.
		} else {
			$psgconf->data_obj('logadm_timestamps')->insert(
				{ $log => $line }
			);
		}
	}
}

sub _add_files
{
	my ($psgconf, $cmd, @files) = @_;
	my ($file, $fullcmd);

	foreach $file ( @files ) {

		### The logadm command will rewrite the logadm_conf_file 
		### with the arguments sorted in alphabetical order, so make
		### sure we do that the first time.
		$fullcmd = ' -C ' . $psgconf->data_obj('logadm_file_cnt')->get();
		$fullcmd .= ' -N '
			if ( ! -f $file );
		$fullcmd .= " -P '" .
			$psgconf->data_obj('logadm_timestamps')->find($file) .  "'"
			if ( $psgconf->data_obj('logadm_timestamps')->exists($file) );
		$fullcmd .= " -a '" . $cmd . "'"
			if ( defined $cmd );
		$fullcmd .= ' -p 1d -z 0';

		$psgconf->data_obj('logadm_files')->add ( $file . $fullcmd );
	}
}

sub _policy_add_syslog_files 
{
	my ($self, $psgconf) = @_;
	my (@logs);

	return
		if ($psgconf->data_obj('syslog_enable')->equals('false')
			|| $psgconf->data_obj('logadm_enable')->equals('false'));

	map {
		push @logs, $_ if ( -f $_ );
	} keys %{$psgconf->data_obj('syslog')->get()};

	&_add_files ($psgconf, 
		"kill -HUP `cat " . $psgconf->data_obj('pidfile_dir')->get() . "/syslog.pid`",
		@logs );
}

sub _policy_add_ftp_files 
{
     my ($self, $psgconf) = @_;

	return
		if ($psgconf->data_obj('anon_ftp_enable')->equals('false')
			|| $psgconf->data_obj('logadm_enable')->equals('false'));

	&_add_files ($psgconf, undef, ( $psgconf->data_obj('log_dir')->get() . '/xferlog' ));
}

sub _policy_add_www_files 
{
     my ($self, $psgconf) = @_;
	my (@logs);

	return
		if ($psgconf->data_obj('www_enable')->equals('false')
			|| $psgconf->data_obj('logadm_enable')->equals('false'));

	@logs = (
		$psgconf->data_obj('www_log_dir')->get() . '/suexec',
		$psgconf->data_obj('www_log_dir')->get() . '/error_log',
		$psgconf->data_obj('www_log_dir')->get() . '/access_log'
	);

	map { push @logs, $_; } values %{$psgconf->data_obj('www_vh_access_log')->get()};

	push @logs, $psgconf->data_obj('www_log_dir')->get() . '/ssl_engine_log'
		if ( $psgconf->data_obj('www_vh_ssl')->count() );

	push @logs, $psgconf->data_obj('www_jk_logfile')->get()
		if ( $psgconf->data_obj('www_jk_enable')->equals('true') );

	###
	### FIXME: Need to add the bluestem and any other logs not in
	### the stock psgconf modules.  This really screams to be put
	### into a method that other Control modules can call from w/i
	### one of their policies.
	###

	&_add_files ( $psgconf, 
		$psgconf->data_obj('www_apachectl_path')->get() . " graceful > /dev/null 2>&1", 
		@logs);
}

###############################################################################
###
###  new() constructor
###
###############################################################################

sub new {
	my ($class, $psgconf) = @_;
	my ($self);
  
	$self = {};
  
	bless($self, $class);
  
	$self->{name} = 'logadm';
	$self->{enable} = $self->{name} . '_enable';
	$self->{packages} = $self->{name} . '_packages';

	$psgconf->register_data(
		'logadm_enable'	=> PSGConf::Data::Boolean->new(
							'value' => 'false'
						),
		'logadm_conf_file'	=> PSGConf::Data::String->new(
							'value_abspath' => 1,
							'value' => '/etc/logadm.conf'
						),
		'logadm_file_cnt'	=> PSGConf::Data::Integer->new(
							'value' => 14
						),
		'logadm_timestamps'	=> PSGConf::Data::Hash->new(),
		'logadm_files'		=> PSGConf::Data::List->new(
							'unique' => 1
						)
	);

	### 
	### Rotate the syslog/ftp/apache logs.  Do not need to do the
	### TSM logs as dsmc does it for us (by setting the retention
	### in the config files).
	### 
	$psgconf->register_policy($self,
		logadm_merge_timestamps		=> '_policy_merge_timestamps',
		logadm_add_syslog_files		=> '_policy_add_syslog_files',
		logadm_add_ftp_files		=> '_policy_add_ftp_files',
		logadm_add_www_files		=> '_policy_add_www_files'
	);

	return $self;
}

###############################################################################
###
###  decide() method
###
###  generate logadm_conf_file based on files specified in psg.conf
###
###############################################################################

sub decide {
	my ($self, $psgconf) = @_;
	my ($conffile);
	
	return
		if ($psgconf->data_obj('logadm_enable')->equals('false'));

	### create logadm_conf_file
	$psgconf->register_actions(
		PSGConf::Action::GenerateFile::Literal->new(
			name		=> $psgconf->data_obj('logadm_conf_file')->get(),
			content	=> join("\n", @{$psgconf->data_obj('logadm_files')->get()}) . "\n",
			backup	=> 0
		)
	)
}

1;

__END__

=head1 NAME

PSGConf::Control::logadm - psgconf control class for controlling log rotation/pruning for Solaris

=head1 SYNOPSIS

In F<psgconf_modules>:

  Control PSGConf::Control::logadm

=head1 DESCRIPTION

The B<PSGConf::Control::logadm> module provides a B<psgconf> control object
for rotating system logs using the logadm facility.
It supports the following methods:

=over 4

=item new()

The constructor.  Its parameter is a reference to the B<PSGConf>
object.  It registers the following data objects:

=over 4

=item I<logadm_enable>

A B<PSGConf::Data::Boolean> object to enable/disable this feature

=item I<logadm_conf_file>

A B<PSGConf::Data::String> object defining the location of the C<logadm>
config file.  Defaults to F</etc/logadm.conf>.

=item I<logadm_file_cnt>

A B<PSGConf::Data::Integer> object for the number of files we need to
keep.  The default is 14.

=item I<logadm_files>

A B<PSGConf::Data::List> object of the commands to use to configure
C<logadm>.

=back

The constructor also registers the following policy methods:

=over 4        

=item I<logadm_add_syslog_files>

Requests that the C<syslog> files be rotated.

=item I<logadm_add_ftp_files>

Requests that the C<anon_ftp> log files be rotated.

=item I<logadm_add_www_files>

Requests that the C<apache> log files be rotated.

=back

=item decide()

Registers a B<PSGConf::Action::GenerateFile::Literal> Action object to
generate the F</etc/logadm.conf> file.

=back

=head1 SEE ALSO

C<CE<lt>logadm(8)E<gt>>

L<perl>

L<PSGConf>

L<PSGConf::Action::GenerateFile::Literal>

L<PSGConf::Data::Boolean>

L<PSGConf::Data::Hash>

L<PSGConf::Data::Integer>

L<PSGConf::Data::List>

L<PSGConf::Data::String>

L<psgconf-intro>

=cut


syntax highlighted by Code2HTML, v. 0.9.1