summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2017-02-10 14:08:32 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2017-02-10 14:08:32 (GMT)
commitb90b1590c9f1575b2f8ca2da8e993661ea16abb3 (patch) (side-by-side diff)
treea626cc0b64f947aaafb61f3bbfead13e40df962f
parent304127cdec228e72ea3b12d3ee1bcd3fc5f0e893 (diff)
downloadsyslogck-b90b1590c9f1575b2f8ca2da8e993661ea16abb3.tar.gz
syslogck-b90b1590c9f1575b2f8ca2da8e993661ea16abb3.tar.bz2
First implementation
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--syslogck224
1 files changed, 182 insertions, 42 deletions
diff --git a/syslogck b/syslogck
index c59cb6d..9a68544 100644
--- a/syslogck
+++ b/syslogck
@@ -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;
+ }
+}
+
+

Return to:

Send suggestions and report system problems to the System administrator.