# -*-perl-*-

# $Id: Syslog.plm,v 1.2 2006/12/19 16:57:24 thomas Exp $

# What package to handle /etc/syslog.conf

# $Log: Syslog.plm,v $
# Revision 1.2  2006/12/19 16:57:24  thomas
# Syslog creates it's own logrotate.d entry
#
# Revision 1.1  2004/03/05 15:16:36  thomas
# Add /Syslog subtree
#


=head1 NAME

What::Node::Syslog -- manage system logging


=head1 DESCRIPTION

The What::Node::Syslog controls the configuration of the
syslog daemon

Nodes under the B<Syslog> root node correspond to lines in the
syslog.conf file.  The node name is a unique key to allow later
modification of the logging instance.  The attributes of each node are
listed below.  Please see the appropriate B<syslog.conf> man page for
a full description of the syslog.conf file.

=over 4

=item selector

The B<selector> attribute specifies a pattern of facilities and priorities
belonging to the specified action.

=item action

The B<action> attribute specifies a destination for the logged
messages.  This can be a file, a remote host (prepended by an '@'
symbol) or any other destination understood by the underlying syslog
daemon.

=item comment

This attribute will by prepended with a '#' symbol and placed on the
line above the syslog entry.

=item norotate

If this attribute has a positive value ("1", "yes", "on", etc.), the
B<action> value will not be included in any logrotate script that gets
written out.

=back

Any triggers attached to the root B<Syslog> node will be run any time
the configuration changes.  A good trigger to place here would be one
that caused the syslog daemon to re-examine its configuration file.

An additional facility exists to automate creation of a logrotate
control file from the syslog data.  If a B<logrotate> attribute exists
at the top level B</Syslog> node, the content of the B</Syslog> node
will be used to create the file given as the value of the B<logrotate>
attribute.  In addition, the special word @@FILELIST@@ will be
substituted with a space-separated list of files indicated in the
B<action> attribute of the B</Syslog> sub-nodes.  Only actions
beginning with a '/' character will be used, and a specific action can
be skipped from the list by including the B<norotate> attribute with
an affirmative value, as indicated above.

=head1 EXAMPLES

Log auth events at severity B<info> or higher to B</var/log/authlog>.

  <node path="/Syslog/auth"
     action="/var/log/authlog"
     selector="auth.info" />

Send all events at seveity B<err> or higer to the log host.

  <node path="/Syslog/loghost_error"
     action="@loghost"
     selector="*.err" />

Disable the previously enabled loghost events.

  <delete path="/Syslog/loghost_error" />

Run /etc/rc.d/init.d/syslog reload (or equivalent) any time
/etc/syslog.conf changes.

  <node path="/Syslog" >
    <trigger path="Init" init="syslog" functions="reload" />
  </node>

=cut

package What::Node::Syslog;

use strict;
use FileHandle;
use Data::Dumper;

use base qw(What::Node);

our %opts = ();

# Set the priority for this module in $::
$::module_priority{'Syslog'} = 20;

###############################################################################
# Apply the current configuration

sub apply {
  my $class = shift;
  my $section = shift;
  my $what = shift;
  my $sname = shift;
  my $cname = shift;

  my ($children, $newconfig, $newfilename, $attrs, $rootattrs);

  $newfilename =
    (defined ($::opts{root}) ? $::opts{root} : '') . '/etc/syslog.conf';

  $rootattrs = $class->getattrs ($section);
  defined ($rootattrs->{minsize}) or $rootattrs->{minsize} = 0;

  $newconfig = <<__Eof__
########
# syslog.conf configuration created by What::Node::Syslog
# Do not edit!  Hand-made changes will be lost!
########

__Eof__
  ;

  $children = $class->getchildren ($section);
  my %rotates;
  foreach (sort keys %$children) {
    $attrs = $children->{$_}->getattrs();
    defined ($attrs->{selector}) && defined ($attrs->{action})
      or next;
    defined ($attrs->{comment}) and $newconfig .= "# $attrs->{comment}\n";
    $newconfig .= sprintf "%-40s %s\n",
      $attrs->{selector}, $attrs->{action};
    $rotates{$attrs->{action}} = 1 if ($attrs->{action} =~ /^\// && ! $class->checkbool ($attrs->{norotate}));
  }

  if ($class->check_and_replace_content ($newfilename,
                                         $newconfig,
                                         $rootattrs->{minsize},
                                         1
                                        )) {
    $class->run_triggers ($section, $what, $cname);
  }

  # Create the logrotate script if requested
  if ($rootattrs->{logrotate}) {
    my $logrotate = $class->gettext ($section);
    if (defined $logrotate) {
      # Do substitutions
      while ($logrotate =~ /\@\@FILELIST\@\@(\S*)/) {
	my $suffix = $1;
	$suffix = '' unless defined $suffix;
	my $filelist = join ' ', (map "$_$suffix", sort keys %rotates);
	$suffix =~ s/([.*])/\\$1/g;
	$logrotate =~ s/\@\@FILELIST\@\@${suffix}/${filelist}/;
      }
      $newfilename = (defined ($::opts{root}) ? $::opts{root} : '') . $rootattrs->{logrotate};

      $class->check_and_replace_content ($newfilename,
                                         $logrotate,
                                         1,
                                         1
                                        )
    }
  }

  return 1;
}


###############################################################################
# Generate a what config from a syslog.conf file

sub generate {
  my $class = shift;
  my $section = shift;
  my $what = shift;
  my $filename = shift;

  # Assume $filenames refers to xinetd config files from filedist
  defined ($filename) or $filename =
    (defined ($::opts{root}) ? $::opts{root} : '') . '/etc/syslog.conf';

  (-f $filename) or return 0;

  my ($i, $attrs) = (0, {});
  my $fh = new FileHandle $filename, 'r';
  if (!defined $fh) {
    $section->err ("Failed to open $filename!\n");
    return 0;
  }
  while (<$fh>) {
    if (s/^\s*\#\s*//) {
      chomp ($attrs->{comment}=$_);
      next;
    }
    ($attrs->{selector}, $attrs->{action}) = split;
    defined ($attrs->{selector}) && defined ($attrs->{action}) or next;
    $class->setattrs ($section, $attrs, $i++, 1);
    $attrs = {};
  }
  return 1;
}


###############################################################################

1;
