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


package PSGConf::Control::syslog;

use strict;

use PSGConf::Action::GenerateFile::EnvFile;
use PSGConf::Action::GenerateFile::Literal;
use PSGConf::Action::GenerateFile::syslog_conf;
use PSGConf::Action::MkDir;
use PSGConf::Action::CreateFile;
use PSGConf::Action::ModifyFile;
use PSGConf::Action::RestartDaemon;
use PSGConf::Data::Boolean;
use PSGConf::Data::Hash;
use PSGConf::Data::List;
use PSGConf::Data::String;

use PSGConf::Control::Packages qw(_add_pkgs);


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

require Exporter;
our @ISA = qw (Exporter);
our %EXPORT_TAGS = ( 'all' => [ qw( _add_syslog) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} });
our @EXPORT = qw();

### To be called from other Control modules
sub _add_syslog
{
	my ($self, $psgconf) = @_;

	if (!exists($self->{enable}) || $self->{enable} eq '' ) {
		warn "\t!!!Control::syslog->_add_syslog(): no enable attribute(s)\n";
		return;
	}

     return
		if ($psgconf->data_obj($self->{enable})->equals('false'));

	### Now check to make sure we are adding defined values in.
	if ( !exists($self->{syslog}) || $self->{syslog} eq ''||
		!exists($self->{facility}) || $self->{facility} eq '') {
		warn "\t!!!Control::syslog->_add_syslog(): no syslog/facility attribute(s)\n";
		return;
	}

     $psgconf->data_obj('syslog')->insert(
          { $self->{syslog} => $self->{facility} }
     ) if (!defined $psgconf->data_obj('syslog')->find($self->{syslog}));
}

### set default location for syslog_dir
sub _policy_default_dir
{
	my ($self, $psgconf) = @_;

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

	$psgconf->data_obj('syslog_dir')->prepend(
		$psgconf->data_obj('log_dir')->get() . '/'
	) if ( ! $psgconf->data_obj('syslog_dir')->match('^/'));
}


### set absolute path to log files
sub _policy_canonify_logfiles
{
	my ($self, $psgconf) = @_;
	my ($log, $new_log);

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

	foreach $log (keys %{$psgconf->data_obj('syslog')->get()})
	{
		if (substr($log, 0, 1) ne '/' && substr($log, 0, 1) ne '@') 
		{
			$new_log = $psgconf->data_obj('syslog_dir')->get() . "/$log";
			$psgconf->data_obj('syslog')->insert(
				{ $new_log => $psgconf->data_obj('syslog')->find($log) }
			);
			$psgconf->data_obj('syslog')->delete($log);
		}
	}
}


### handle platforms with "authpriv" facility
sub _policy_add_authpriv_facility
{
	my ($self, $psgconf) = @_;
	my ($target, $value);

	return
		if ($psgconf->data_obj('syslog_enable')->equals('false')
		    || $psgconf->data_obj('platform')->match('solaris'));

	foreach $target (sort keys %{$psgconf->data_obj('syslog')->get()})
	{
		$value = $psgconf->data_obj('syslog')->find($target);
		$value =~ s/\bauth\b/$&,authpriv/; 

		$psgconf->data_obj('syslog')->insert(
			{ $target => $value }
		);
	}
	foreach $target (sort keys %{$psgconf->data_obj('syslog_literal')->get()})
	{
		$value = $psgconf->data_obj('syslog_literal')->find($target);
		$value =~ s/\bauth\b/$&,authpriv/; 

		$psgconf->data_obj('syslog_literal')->insert(
			{ $target => $value }
		);
	}
}


### expand USED_FACILITIES token
sub _policy_expand_used_facilities
{
	my ($self, $psgconf) = @_;
	my ($log, $levels, $facilities, %used_facilities, $default);

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

	foreach $log (keys %{$psgconf->data_obj('syslog')->get()})
	{
		if ($psgconf->data_obj('syslog')->find($log) =~ m/USED_FACILITIES/)
		{
			$default = $log;
			next;
		}

		foreach $levels (split(';', $psgconf->data_obj('syslog')->find($log)))
		{
			$levels =~ s/\.\w+//;
			map {
				$used_facilities{$_}++;
			} split(',', $levels);
		}
	}

	if (defined($default))
	{
		$facilities = join(',', sort keys %used_facilities);
		$log = $psgconf->data_obj('syslog')->find($default);
		$log =~ s/USED_FACILITIES/$facilities/;
		$psgconf->data_obj('syslog')->insert(
			{ $default => $log }
		);
	}
}


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

	map {
		$psgconf->data_obj('syslog')->insert(
			{ '@' . $_ => '*.info' }
		);
	} @{$psgconf->data_obj('syslog_servers')->get()};
}

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

	if ( $psgconf->data_obj('syslog_enable')->equals('true') ) {
     	$psgconf->data_obj('rc_scripts')->insert(
			{ 'syslogd' => { 'state' => 'enable' }}
		);

		if ( $psgconf->data_obj('platform')->match('freebsd') ) {
			$args = 
				$psgconf->data_obj('syslog_remote_enable')->equals('true')
					? ''
					: ($psgconf->data_obj('syslog_servers')->count()
						### Turn off accepting packets remotely
						? '-s '

						### Turn off sending packets remotely, since
						### we do not have any syslog_servers defined.
						: '-ss ');

     		$psgconf->data_obj('rc_vars')->insert(
				{ 'syslogd_flags' => $args . '-vv' }
			);
		}
	}
}

###############################################################################
###  ModfileFile plugin to edit /etc/rc.tcpip
###############################################################################

sub _modify_rc_tcpip {
	my ($action, $infh, $fh, $psgconf) = @_;
	my ($line);

	while ($line = <$infh>) {

		$line = 'start /usr/sbin/syslogd "$src_running"' .
			($psgconf->data_obj('syslog_remote_enable')->equals('true')
			? '': ' -r') . "\n"
			if ($line =~ m|^start /usr/sbin/syslogd "\$src_running"( -r)?$| );

		print $fh $line;
	}

	return 1;
}

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

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

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

	$psgconf->register_actions(
		PSGConf::Action::GenerateFile::syslog_conf->new(
			'name'		=> $psgconf->data_obj('syslog_file')->get(),
			'description'	=> 'syslogd configuration file',
			'syslog'	=> {
						%{$psgconf->data_obj('syslog')->get()},
						%{$psgconf->data_obj('syslog_literal')->get()}
					}
		),
		PSGConf::Action::MkDir->new(
			'name'		=> $psgconf->data_obj('syslog_dir')->get()
		)
	);

	foreach $file ( keys %{$psgconf->data_obj('syslog')->get()}) {
		if (substr($file, 0, 1) eq '/') {
			$psgconf->register_actions(
				PSGConf::Action::CreateFile->new(
					'name'	=> $file
				)
			);
		}
	}

	### Turn off accepting packets remotely
	$psgconf->register_actions(
		PSGConf::Action::GenerateFile::Literal->new(
			'name'		=> '/etc/default/syslogd',
			'content'		=> 'LOG_FROM_REMOTE='  .
				($psgconf->data_obj('syslog_remote_enable')->equals('true')
				? 'YES' : 'NO') . "\n"
		)
	) if ( $psgconf->data_obj('platform')->match('solaris'));

	$psgconf->register_actions(
		PSGConf::Action::GenerateFile::EnvFile->new(
			'name'		=> '/etc/sysconfig/syslog',
			'quote_values'	=> 1,
			'vars'		=> {
							'SYSLOGD_OPTIONS'	=> '-m 0' . ($psgconf->data_obj('syslog_remote_enable')->equals('true')? ' -r': ''),
							'KLOGD_OPTIONS'	=> '-x'
						}
		)
	) if ($psgconf->data_obj('platform')->match('(-rhel-)|(-rhl-)|(-fc-)'));

	### Turn off accepting packets remotely
	$psgconf->register_actions(
		PSGConf::Action::ModifyFile->new(
			'name'         => '/etc/rc.tcpip',
			'modify_func'  => \&_modify_rc_tcpip,
			'uid'          => 0,
			'gid'          => 0,
			'mode'         => 0774
		)
	) if ( $psgconf->data_obj('platform')->match('aix'));

	### Make sure we restart syslogd after we create all the files 
	$psgconf->register_actions(
		PSGConf::Action::RestartDaemon->new(
			name           => 'syslogd',
			filename       => [ $psgconf->data_obj('syslog_dir')->get(),
							$psgconf->data_obj('syslog_file')->get() ],
			pidfile		=> $psgconf->data_obj('pidfile_dir')->get() . '/syslog.pid'
		)
	);
}



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

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

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

	### So that _add_pkgs knows which directives to look at
	$self->{name} = 'syslog';
	$self->{enable} = $self->{name} . '_enable';
	$self->{packages} = $self->{name} . '_packages';

	$psgconf->register_data(
		'syslog_enable'		=> PSGConf::Data::Boolean->new(
						value => 'true'
					),
		'syslog_remote_enable'	=> PSGConf::Data::Boolean->new(
						value => 'false'
					),
		'syslog'		=> PSGConf::Data::Hash->new(),
		'syslog_file'		=> PSGConf::Data::String->new(
						'value_abspath' => 1,
						'value' => '/etc/syslog.conf'
					),
		'syslog_dir'		=> PSGConf::Data::String->new(
						'value' => 'syslog'
					),
		'syslog_literal'	=> PSGConf::Data::Hash->new(),
		'syslog_packages'	=> PSGConf::Data::List->new(),
		'syslog_servers'	=> PSGConf::Data::List->new()
	);

	$psgconf->register_policy($self,
		syslog_default_dir	=> '_policy_default_dir',
		syslog_canonify_logfiles
					=> '_policy_canonify_logfiles',
		syslog_add_authpriv_facility
					=> '_policy_add_authpriv_facility',
		syslog_expand_used_facilities
					=> '_policy_expand_used_facilities',
		syslog_enable_rc_scripts
					=> '_policy_enable_rc_scripts',
		syslog_add_packages	=> '_add_pkgs',
		syslog_add_remote_servers => '_policy_add_remote_servers'
	);

	return $self;
}


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

1;

__END__

=head1 NAME

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

=head1 SYNOPSIS

In F<psgconf_modules>:

  Control PSGConf::Control::syslog

=head1 DESCRIPTION

The B<PSGConf::Control::syslog> module provides a B<psgconf> control object
for configuring C<syslogd>.  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<syslog_enable>

A B<PSGConf::Data::Boolean> object that indicates whether C<syslog>
should be configured.  The default is true.

=item I<syslog_remote_enable>

A B<PSGConf::Data::Boolean> object that indicates whether C<syslog>
should accept remote packats.  The default is false.

=item I<syslog_packages>

A B<PSGConf::Data::List> object that lists all packages to install.

=item I<syslog>

A B<PSGConf::Data::Hash> object that contains log file entries for
F<syslog.conf>.  The hash key is the name of the file to create,
and the value is the facility and priority specification.

=item I<syslog_file>

A B<PSGConf::Data::String> object that contains the path for the
syslog config file.  The default is F</etc/syslog.conf>.

=item I<syslog_dir>

A B<PSGConf::Data::String> object that contains the path to the
directory where log files will be stored.  If not an absolute path, it
is relative to the value of the I<log_dir> object, which is supplied by
the B<PSGConf::Control::Core> module.

=item I<syslog_servers>

A B<PSGConf::Data::List> object that lists all remote syslog servers
to send data to.

=item I<syslog_literal>

A B<PSGConf::Data::Hash> object that contains non-file entries for
I<syslog_file>.  The hash key is the target (e.g., a login name or
a C<*>), and the value is the facility and priority specification.

=back

The constructor also registers the following policy methods:

=over 4

=item I<syslog_default_dir>

If I<syslog_dir> is not set to an absolute path, prepend the value of
the I<log_dir> object, which is provided by the
B<PSGConf::Control::Core> module.

=item I<syslog_canonify_logfiles>

For each entry in the I<syslog> object that is not an absolute path,
prepend the value of the I<syslog_dir> object.

=item I<syslog_add_authpriv_facility>

On platforms other than Solaris, add the C<authpriv> facility to any
entry in I<syslog> or I<syslog_literal> that already specifies the
C<auth> facility.

=item I<syslog_expand_used_facilities>

Expands the token C<USED_FACILITIES> in the I<syslog> data object with
the list of facilities used by other entries in I<syslog>.

=item I<syslog_enable_rc_scripts>

Modifies the I<rc_scripts> data object (provided by
B<PSGConf::Control::InitScripts>) to enable C<syslogd>.
Also modifies the I<rc_vars> data object (provided by
B<PSGConf::Control::FreeBSD>) to set the C<syslogd> startup
flags to be C<-s -vv> or C<-ss -vv> if there are or are not
I<syslog_servers> defined, respectively.

=back

=item decide()

If the I<syslog_enable> data object is set, instantiates and registers
action objects, as follows:

=over 4

=item *

Registers a new B<PSGConf::Action::GenerateFile::syslog_conf> object to
create I<syslog_file>.

=item *

Registers a new B<PSGConf::Action::GenerateFile::Literal> object to
create I</etc/default/syslogd> on C<Solaris> systems.

=item *

Registers a new B<PSGConf::Action::MkDir> object to create the
I<syslog_dir> directory.

=item *

Registers new B<PSGConf::Action::CreateFile> objects for each file
in the I<syslog> object.

=item *

Registers new B<PSGConf::Action::ModifyFile> object for the
F</etc/rc.tcpip> file on AIX systems to add the C<-r> flag
to the startup of C<syslogd>.

=back

=back

=head1 SEE ALSO

syslog.conf(4)

L<perl>

L<PSGConf>

L<PSGConf::Action::GenerateFile::EnvFile>

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

L<PSGConf::Action::GenerateFile::syslog_conf>

L<PSGConf::Action::MkDir>

L<PSGConf::Action::CreateFile>

L<PSGConf::Action::ModifyFile>

L<PSGConf::Action::RestartDaemon>

L<PSGConf::Control::Core>

L<PSGConf::Control::InitScripts>

L<PSGConf::Control::Packages>

L<PSGConf::Data::Boolean>

L<PSGConf::Data::Hash>

L<PSGConf::Data::List>

L<PSGConf::Data::String>

L<psgconf-intro>

=cut



syntax highlighted by Code2HTML, v. 0.9.1