###
###  Copyright 2000-2007 University of Illinois Board of Trustees
###  All rights reserved. 
###
###  swatch.pm - swatch module for psgconf
###
###  Campus Information Technologies and Educational Services
###  University of Illinois at Urbana-Champaign
###


package PSGConf::Control::swatch;

use strict;

use PSGConf::Action::GenerateFile::Literal;
use PSGConf::Data::Boolean;
use PSGConf::Data::List;
use PSGConf::Data::String;
use PSGConf::Control::Packages qw(_add_pkgs);


###############################################################################
###  policy methods
###############################################################################

###
### WARNING: Needs to be called after syslog_default_dir policy is called,
### because syslog_dir may get prepended there.
###
sub _policy_add_crontab
{
     my ($self, $psgconf) = @_;
	my ($cmd);

     return
          if ( ! -f $psgconf->data_obj('swatch_cmd')->get() ||
			$psgconf->data_obj('swatch_enable')->equals('false'));

	$cmd = $psgconf->data_obj('swatch_cmd')->get()
			. ' -c /usr/local/etc/swatchrc -f '
			. $psgconf->data_obj('syslog_dir')->get()
			. '/messages > /dev/null 2>&1';

     $psgconf->data_obj('crontabs')->insert(
		{'root' => [{
				hour => $psgconf->data_obj('swatch_hour')->get(),
				minute => $psgconf->data_obj('swatch_minute')->get(),
         			command   => $cmd
			}]
		}
     );
}

###
### WARNING: This is a fix because Solaris systems complain about a host
### being our address, when you look into the MAC address it reports, that
### is the same host's MAC address.
###
sub _policy_add_swatch_rule
{
     my ($self, $psgconf) = @_;
	my (@mac, @ip, $i, @res, $line, $tmp);

	### If we are not running swatch nor on Solaris, just return
     return
          if ( ! $psgconf->data_obj('platform')->match('solaris') ||
			$psgconf->data_obj('swatch_enable')->equals('false'));

	### find all the IP/MAC pairs that we are currently using
	$i=-1;
	@res = `/sbin/ifconfig -au`;
	foreach $line ( @res ) {
		if ( $line =~ /^\s+inet / ) {
			$i++;
			$tmp=$line;
			$tmp =~ s/^\s*inet //;
			$tmp =~ s/\s.*$//;
			$ip[$i] = &_format_addr($tmp, 'IP');
		}

		if ( $line =~ /^\s+ether / ) {
			my ($tmp) = $line;
			$tmp =~ s/^\s*ether //;
			$mac[$i] = &_format_addr($tmp, 'MAC');
		}
	}

	### Now assemble the swatch configuration for all the IP/MAC pairs
	$line=undef;
	for ($i = 0; $i < scalar (@ip); $i++) {
		$line .= "ignore / WARNING: IP: Hardware address "
			. $mac[$i]
			. " trying to be our address "
			. $ip[$i]
			. "!/\n"
			if ( defined $ip[$i] && defined $mac[$i] );
	}
	$psgconf->data_obj('swatch_config')->add(
		"# Ignore warnings reported between my IP and MAC address\n"
		. $line
		. "	continue"
	) if ( defined $line );
}

sub _format_addr {
	my ($str, $type) = @_;
	my ($res, @addr, $sep, $cnt, $t, $digits);

	if ( $type eq 'IP' ) {
		$sep = '.'; $cnt = 4; $digits = 3; $t = 'd';
	} elsif ( $type eq 'MAC' ) {
		$sep = ':'; $cnt = 6; $digits = 2; $t = 'x';
	} else {
		return undef;
	}

	@addr = split (/\Q$sep\E/, $str);

	return undef
		if ( $cnt != scalar (@addr) );

	$res = undef;
	for (my $i = 0; $i < scalar (@addr); $i++) {
		$res .= $sep if ( defined $res );
		$res .= sprintf "%${digits}.${digits}$t",
			($type eq 'MAC')
				? hex($addr[$i])
				: $addr[$i];
	}

	return $res;
};

###############################################################################
###  Constructor
###############################################################################

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

	$self = {};
	bless($self, $class);

	$self->{name} = 'swatch';
	$self->{enable} = $self->{name} . '_enable';
	$self->{packages} = $self->{name} . '_packages';

	$psgconf->register_data(
		swatch_enable	=> PSGConf::Data::Boolean->new(
							value => 'false'
						),
		swatch_config	=> PSGConf::Data::List->new(),
		swatch_cmd	=> PSGConf::Data::String->new(
							'value_abspath' => 1,
							value => '/usr/local/bin/swatch'
						),
		swatch_mailto	=> PSGConf::Data::String->new(
							value => 'root'
						),
		swatch_hour	=> PSGConf::Data::String->new(
							value => '23'
						),
		swatch_minute	=> PSGConf::Data::String->new(
							value => '58'
						),
		swatch_packages	=> PSGConf::Data::List->new()
	);

	$psgconf->register_policy($self,
		swatch_add_packages	=> '_add_pkgs',
		swatch_add_crontab	=> '_policy_add_crontab',
		swatch_add_rule	=> '_policy_add_swatch_rule'
	);

	return $self;
}


##############################################################################
###  decide() method
###############################################################################

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

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

	$psgconf->register_actions(
		PSGConf::Action::GenerateFile::Literal->new(
			name      	=> '/usr/local/etc/swatchrc',
			mode      	=> 0640,
			description	=> 'Swatch Configuration File',
			content   	=> join ("\n\n", 
						@{$psgconf->data_obj('swatch_config')->get()})
						. "\n\nwatchfor /.*/\n\tmail addresses="
						. $psgconf->data_obj('swatch_mailto')->get()
						. ",subject=Message from Swatch on "
						. $psgconf->data_obj('hostname')->get()
						. ",keep_open=1\n"
		)
	);
}

###############################################################################
###  documentation
###############################################################################

1;

__END__

=head1 NAME

PSGConf::Control::swatch - psgconf control class for swatch syslog reporting

=head1 SYNOPSIS

In F</etc/psgconf_modules>:

  Control PSGConf::Control::swatch

=head1 DESCRIPTION

The B<PSGConf::Control::swatch> module provides a B<psgconf> control object
for configuring swatch syslog reporting.  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<swatch_enable>

A B<PSGConf::Data::Boolean> object, indicating whether swatch should
be configured.  The default is no.

=item I<swatch_packages>

A B<PSGConf::Data::List> object containing a list of packages to install.

=item I<swatch_config>

A B<PSGConf::Data::List> object containing a list of swatch actions to
put into the F<swatchrc> file.

=item I<swatch_mailto>

A B<PSGConf::Data::String> object containing an email address
to send the results of the C<swatch> command to.

=item I<swatch_cmd>

A B<PSGConf::Data::String> object containing the full command to the
F<swatch> binary.  Defaults to F</usr/local/bin/swatch>.

=item I<swatch_hour>

A B<PSGConf::Data::String> object containing which hour or hours
to run F<swatch>.  Defaults to C<23>.

=item I<swatch_minute>

A B<PSGConf::Data::String> object containing which minute or minutes
to run F<swatch>.  Defaults to C<58>.

=back

The constructor also registers the following policy methods:

=over 4

=item I<swatch_add_packages>

If I<swatch_enable> is true, adds the B<swatch> package and its
requirements to the I<pkg_install_list> Data object (provided by
B<PSGConf::Control::Packages>).

=item I<bass_add_crontab>

If I<swatch_enable> is true, adds an entry for the I<swatch> nightly
to I<root>'s I<crontab> Data object (provided by B<PSGConf::Control::cron>).

=item decide()

Does the following:

If I<swatch_enable> is true, creates the F</usr/local/etc/swatchrc>
config file for the C<swatch> command to use.

=back

=back

=head1 SEE ALSO

L<perl>

L<PSGConf>

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

L<PSGConf::Control::cron>

L<PSGConf::Control::Packages>

L<PSGConf::Data::Boolean>

L<PSGConf::Data::List>

L<PSGConf::Data::String>

L<psgconf-intro>

=cut



syntax highlighted by Code2HTML, v. 0.9.1