#!/usr/bin/perl
-# Copyright (c) 2010 Peter Palfrader <peter@palfrader.org>
+# Copyright (c) 2010,2012 Peter Palfrader <peter@palfrader.org>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
use strict;
use warnings;
+use threads;
+
use English;
use Getopt::Long;
use FindBin qw($Bin);
return $ticks;
}
-my $USAGE = "Usage: $PROGRAM_NAME [--help] | [--warn=<nn>] [--critical=<nn>] [--geozonedir=<geodir>] <indir>\n";
-my $params = { 'warn' => '14d', 'critical' => '7d' };
+sub check_one {
+ $SIG{'KILL'} = sub { threads->exit(); };
+
+ my $zone = shift;
+ my $check = shift;
+ my $extra = shift;
+ my $params = shift;
+
+ my @cmd = ($check, '-w', $params->{'warn'}, '-c', $params->{'critical'});
+ push(@cmd, '-r', $extra->{'initial_refs'}) if exists $extra->{'initial_refs'};
+ push(@cmd, '-d') if $params->{'debug'};
+ push(@cmd, $zone);
+ open(P, '-|', @cmd) or die ("Cannot run $CHECK for $zone\n");
+ my @p = <P>;
+ close P;
+ $p[0] = $zone.': '. $p[0] if (scalar @p > 0);
+
+ my $res = $CHILD_ERROR >> 8;
+
+ return ($res, \@p);
+}
+
+my $USAGE = "Usage: $PROGRAM_NAME [--help] | [--debug] [--timeout=<nn>] [--warn=<nn>] [--critical=<nn>] [--geozonedir=<geodir>] <indir>\n";
+my $params = { 'timeout' => 45, 'warn' => '14d', 'critical' => '7d' };
Getopt::Long::config('bundling');
GetOptions (
'--help' => \$params->{'help'},
+ '--timeout=i' => \$params->{'timeout'},
'--warn=s' => \$params->{'warn'},
+ '--debug' => \$params->{'debug'},
'--critical=s' => \$params->{'critical'},
'--geozonedir=s' => \$params->{'geozonedir'},
) or die ($USAGE);
if ($params->{'help'}) {
- print $USAGE;
- exit(0);
+ print $USAGE;
+ exit(0);
};
die ($USAGE) unless (scalar @ARGV == 1);
my $INDIR = shift;
-my $count =
- { 'ok' => [],
- 'warn' => [],
- 'critical' => [],
- 'unknown' => [],
- 'unsigned' => [],
- };
+my $states = [qw{critical warn unknown ok unsigned}];
+my $count = { map { $_ => [] } @$states };
+my $details = { map { $_ => [] } @$states };
-my @dnsseczones;
+my %dnsseczones;
# load list of classic zones that will do DNSSEC
chdir $INDIR or die "chdir $INDIR failed? $!\n";
opendir INDIR, '.' or die ("Cannot opendir $INDIR\n");
for my $file (sort {$a cmp $b} (readdir INDIR)) {
next if ( -l "$file" );
next unless ( -f "$file" );
- next if $file =~ /^(dsset|keyset)-/;
- my $do_dnssec = 0;
+ my $do_dnssec = 1;
+ my $delegated = 1;
+ my $initial_refs = undef;
open(F, '<', $file) or die ("Cannot open $file: $!\n");
for (<F>) {
- if (/^; wzf:\s*dnssec\s*=\s*1\s*$/) { $do_dnssec = 1; last; }
+ if (/^; wzf:\s*dnssec\s*=\s*0\s*$/) { $do_dnssec = 0; }
+ if (/^; delegated\s*=\s*no\s*$/) { $delegated = 0; }
+ if (/^; check-initial-refs\s*=\s*(.*?)\s*$/) { $initial_refs = $1; }
};
close F;
- if ($do_dnssec) {
- push @dnsseczones, $file;
+ if ($do_dnssec && $delegated) {
+ die "Duplicate zone $file?\n" if exists $dnsseczones{$file};
+ $dnsseczones{$file} = {};
+ $dnsseczones{$file}->{'initial_refs'} = $initial_refs if defined $initial_refs;
} else {
- push @{$count->{'unsigned'}}, $file;
+ push @{$count ->{'unsigned'}}, $file;
+ push @{$details->{'unsigned'}}, "$file: marked unsigned or undelegated.\n";
};
}
closedir(INDIR);
for my $file (sort {$a cmp $b} (readdir INDIR)) {
next unless $file =~ /\.zone$/;
- open (F, '<', $file) or die "Cannot open $file: $!\n";
- my ($zc, undef, undef) = Load(join "", (<F>));
- close F;
-
my $zone = basename($file, '.zone');
-
- if ($zc->{'dnssec'}) {
- push @dnsseczones, $zone;
- } else {
- push @{$count->{'unsigned'}}, $zone;
- };
+ die "Duplicate zone $zone?\n" if exists $dnsseczones{$zone};
+ $dnsseczones{$zone} = {};
}
closedir(INDIR);
}
+my %threads;
+for my $zone (sort {$a cmp $b} keys %dnsseczones) {
+ die "Duplicate zone $zone?\n" if defined $threads{$zone};
+ my $thr = threads->create({'context' => 'list'},
+ \&check_one, $zone, $CHECK, $dnsseczones{$zone}, $params);
+ $threads{$zone} = $thr;
+}
-my @details;
+my $begin = time;
+while (time - $begin <= $params->{timeout}) {
+ for my $zone (sort {$a cmp $b} keys %threads) {
+ next unless $threads{$zone}->is_joinable();
-for my $zone (sort {$a cmp $b} @dnsseczones) {
+ my ($res, $det) = $threads{$zone}->join();
- open(P, '-|', ($CHECK, '-w', $params->{'warn'}, '-c', $params->{'critical'}, $zone)) or die ("Cannot run $CHECK for $zone\n");
- my @p = <P>;
- close P;
- $p[0] = $zone.': '. $p[0] if (scalar @p > 0);
- push @details, @p;
+ my $type = ($res == 0) ? 'ok' :
+ ($res == 1) ? 'warn' :
+ ($res == 2) ? 'critical' :
+ 'unknown';
- my $res = $CHILD_ERROR >> 8;
- if ($res == 0) { push @{$count->{'ok'}}, $zone; }
- elsif ($res == 1) { push @{$count->{'warn'}}, $zone; }
- elsif ($res == 2) { push @{$count->{'critical'}}, $zone; }
- else { push @{$count->{'unknown'}}, $zone; };
-};
+ push @{$details->{$type}}, @$det;
+ push @{$count ->{$type}}, $zone;
+ delete $threads{$zone};
+ }
+ last if scalar keys %threads == 0;
+ print STDERR (scalar keys %threads), " threads left: ", join(" ", keys %threads), "\n" if $params->{'debug'};
+ sleep 1;
+}
+for my $zone (sort {$a cmp $b} keys %threads) {
+ push @{$count ->{'warn'}}, $zone;
+ push @{$details->{'warn'}}, "$zone: timeout during check\n";
+ $threads{$zone}->kill('KILL')->detach();
+}
-my $exit;
+my $exit = 0;
my %state_mapping = (
'unknown' => 255,
'critical' => 2,
'warn' => 1,
+ 'unsigned' => 0,
'ok' => 0 );
-for my $state (sort {$state_mapping{$b} <=> $state_mapping{$a}} keys %state_mapping) {
+for my $state (@$states) {
+ @{$count->{$state}} = sort {$a cmp $b} @{$count->{$state}};
+ @{$details->{$state}} = sort {$a cmp $b} @{$details->{$state}};
+
if (scalar @{$count->{$state}}) {
printf "%s: %d", uc($state), scalar @{$count->{$state}};
if ($state_mapping{$state} > 0) {
print ": ", join(', ', @{$count->{$state}});
};
print "; ";
- $exit = $state_mapping{$state} unless defined $exit;
+ $exit = $state_mapping{$state} if ($state_mapping{$state} > $exit);
};
};
printf "unsigned: %d", scalar @{$count->{'unsigned'}};
print "\n";
-print $_ for (@details);
+for my $state (@$states) {
+ for (@{$details->{$state}}) {
+ s/\|/;/g;
+ print $_;
+ }
+}
exit $exit;