diff options
-rw-r--r-- | syslogck | 224 |
1 files changed, 182 insertions, 42 deletions
@@ -2,108 +2,248 @@ use strict; use Sys::Syslog qw(:standard :macros); +use File::Basename; +use Pod::Usage; +use Pod::Man; +use Getopt::Long qw(:config gnu_getopt no_ignore_case); + use Data::Dumper; +use constant { + EX_OK => 0, + EX_USAGE => 64, + EX_DATAERR => 65, + EX_NOINPUT => 66, + EX_OSFILE => 72, + EX_TEMPFAIL => 74, + EX_NOPERM => 77 +}; + +my $progname = basename($0); +my $progdescr = 'FIXME'; +my $tag = $progname; +my $timeout = 10; + +my $debug; +my $dry_run; + my $facility = 'user'; -my %prio_order = ('debug' => 0, - 'info' => 1, - 'notice' => 2, - 'warn' => 3, - 'warning' => 3, - 'err' => 4, - 'error' => 4, - 'crit' => 5, - 'alert' => 6, - 'emerg' => 7, - 'panic' => 7); +my %prio_order = ('debug' => LOG_DEBUG, + 'info' => LOG_INFO, + 'notice' => LOG_NOTICE, + 'warn' => LOG_WARNING, + 'warning' => LOG_WARNING, + 'err' => LOG_ERR, + 'error' => LOG_ERR, + 'crit' => LOG_CRIT, + 'alert' => LOG_ALERT, + 'emerg' => LOG_EMERG, + 'panic' => LOG_EMERG); + +my $priority = $prio_order{notice}; + +sub error { + my $msg = shift; + local %_ = @_; + print STDERR "$progname: " if defined($progname); + print STDERR "$_{prefix}: " if defined($_{prefix}); + print STDERR "$msg\n" +} -my $priority = $prio_order{info}; +sub debug { + error(join(' ',@_), prefix => 'DEBUG') if $debug; +} +sub abend { + my $code = shift; + print STDERR "$progname: " if defined($progname); + print STDERR "@_\n"; + exit $code; +} + sub match_selector { - my ($sel) = @_; + my ($sel, $file, $line, $lc) = @_; my $match; + my @report; + $sel =~ s/\s+//g; - print "matching $sel\n"; foreach my $ent (split /;/, $sel) { - print " ent=$ent\n"; - if ($ent =~ /^(?<fac>.+)\.(?<pri>.*)$/) { - print " f=$+{fac},p=$+{pri}\n"; + if ($ent =~ /^(?<fac>.*)\.(?<pri>.*)$/) { if (match_facility($+{fac})) { if ($+{pri} eq 'none') { + push @report, [ $ent, 0 ] if $debug && $match; $match = 0; - } elsif (match_priority($+{pri})) { - $match = 1; + } elsif (match_priority($+{pri}, \$match)) { + push @report, [ $ent, $match ] if $debug; } } } - print "M $match\n" } - print ($match ? "+MATCH\n" : "-NOPE\n"); + + if (@report) { + my $loc = "$file:$line"; + $loc .= "-".($line+$lc) if $lc; + debug("$loc: " + . ($match ? "select" : "skip") + . " (" + . join('; ', + map { ($_->[1] ? "select" : "skip")." by $_->[0]" } @report) + .")"); + } + return $match; } sub match_facility { my ($arg) = @_; + return 1 if $arg eq ''; foreach my $f (split /,/, $arg) { $f =~ s/\..*//; - print " f=$f\n"; return 1 if $f eq '*' || $f eq $facility; } return 0; } sub match_priority { - my ($pri) = @_; - my $match = 0; - - print " p=$pri :: "; + my ($pri, $prev_match) = @_; + my $match = $$prev_match; + my $neg = $pri =~ s/^!(.+)/$1/; - print " not " if ($neg); + return 0 if $neg && !$match; + my $eq = $pri =~ s/^=(.+)/$1/; if ($pri eq '*') { - print "*"; $match = 1; } else { next unless exists($prio_order{$pri}); if ($eq) { - print $prio_order{$pri}." == $priority"; $match = $prio_order{$pri} == $priority; } else { - print $prio_order{$pri}." <= $priority"; - $match = $prio_order{$pri} <= $priority; + $match = $prio_order{$pri} >= $priority; } } $match = !$match if $neg; - print ":: $match\n"; - return $match; + my $ret = $match != $$prev_match; + $$prev_match = $match; + return $ret; } sub find_actions { - my $file = shift; - my @actions; + my ($file, $actions) = @_; + my $line; # current line number + my $lc; # counter of continuation lines if (open(my $fd, '<', $file)) { while (<$fd>) { + ++$line; chomp; s/^\s+//; next if /^#/; if (/\\$/) { chop; $_ .= <$fd>; + ++$lc; redo; } if (/^(?<sel>.+?)\s+(?<buf>-?)(?<stream>[^\s]+)$/) { - push @actions, $+{stream} if match_selector($+{sel}); + push @{$actions}, $+{stream} + if match_selector($+{sel}, $file, $line - $lc, $lc); } + $lc = 0; } } else { - warn "can't open $file: $!"; + error("can't open $file: $!"); return undef; } - return @actions; + return 1; } -my @act = find_actions("/etc/syslog.conf"); -print Dumper([ @act ]); - - +GetOptions("h" => sub { + pod2usage(-message => "$progname: $progdescr", + -exitstatus => EX_OK); + }, + "help" => sub { + pod2usage(-exitstatus => EX_OK, -verbose => 2); + }, + "usage" => sub { + pod2usage(-exitstatus => EX_OK, -verbose => 0); + }, + "debug|d" => \$debug, + "priority|p=s" => sub { + my ($opt, $arg) = @_; + if ($arg =~ /^(.+?)\.(.+)$/) { + abend(EX_USAGE, "unknown priority") + unless exists($prio_order{$2}); + $priority = $prio_order{$2}; + $facility = $1; + } else { + $facility = $arg; + } + }, + "dry-run|n" => \$dry_run, + "tag|t=s" => \$tag, + "timeout|T=n" => \$timeout +) or exit EX_USAGE; + +$debug++ if $dry_run; + +unshift @ARGV, "/etc/syslog.conf" unless @ARGV; + +my @act; + +foreach my $file (@ARGV) { + find_actions($file, \@act); +} + +if (@act) { + debug("actions: ".join(', ', @act)); + @act = grep { m#^/# } @act; + debug("files: ".join(', ', @act)); +} + +exit EX_OK if $dry_run; + +abend(EX_OSFILE, "no files") unless @act; + +my $file; +while ($file = shift @act) { + last if -r $file; +} + +abend(EX_NOPERM, "no readable files found") unless defined $file; + +debug("selected file: $file"); +open(my $fd, '<', $file) + or abend(EX_NOINPUT, "cannot open file $file: $!"); + +seek $fd, 0, 2; +my $msg; + +eval { + use Time::HiRes qw(time); +}; + +$msg = time() . '-' . $$; + +debug("sending \"$msg\""); +openlog($tag, 'pid', $facility) + or die "openlog failed: $!"; +syslog($priority, $msg); +closelog(); + +my $code = EX_TEMPFAIL; +$SIG{ALRM} = sub { + abend($code, "doesn't match"); +}; +alarm $timeout; +while (1) { + while (<$fd>) { + chomp; + debug("read $_"); + s/\s+$//; + exit(EX_OK) if /$msg$/; + $code = EX_DATAERR; + } +} + + |