close(F);
chomp(@lines);
- shift @lines while ($lines[0] !~ /\+\+\+/);
- shift @lines;
+ my $line;
+ my $has_arch = 0;
+ while (defined($line = shift @lines) && ($line !~ /\+\+\+/)) {
+ if ($line =~ /Architecture/) { $has_arch = 1; }
+ }
my %pkgs;
- for my $line (@lines) {
- my ($state, $pkg, $version, undef) = split(/ */, $line);
- $pkgs{$state}{$pkg} = { 'installed' => $version }
+ for $line (@lines) {
+ my ($state, $pkg, $version, $arch, undef) = split(/ */, $line);
+ $arch = '' unless $has_arch;
+ $pkgs{$state}{$pkg} = { 'installed' => $version, arch => $arch }
}
my $installed = $pkgs{'ii'};
delete $pkgs{'ii'};
+ my @installed_packages = keys(%$installed);
+ my @cmd = ("apt-cache", "policy", @installed_packages);
+
open my $olderr, ">&STDERR" or die "Can't dup STDERR: $!";
open STDERR, ">/dev/null" or die "Can't dup STDOUT: $!";
-
- open (F, "apt-cache policy ".(join(" ", keys(%$installed)))." |") or die ("Cannot run apt-cache policy: $!\n");
+ open (F, "-|", @cmd) or die ("Cannot run apt-cache policy: $!\n");
@lines = <F>;
close(F);
- chomp(@lines);
open STDERR, ">&", $olderr or die "Can't dup OLDERR: $!";
+ chomp(@lines);
- my $line;
my $pkgname = undef;
+ my $candidate_found = 0;
while (defined($line = shift @lines)) {
if ($line =~ /^([^ ]*):$/) {
+ # when we have multi-arch capable fu, we require that
+ # apt-cache policy output is in the same order as its
+ # arguments.
+ #
+ # We need this, because the output block in apt-cache
+ # policy does not show the arch:
+ #
+ # | weasel@stanley:~$ apt-cache policy libedit2:amd64
+ # | libedit2:
+ # | Installed: 2.11-20080614-5
+ # | Candidate: 2.11-20080614-5
+ #
+ # We replace the package name in the output with the
+ # one we asked for ($pkg:$arch) - but to match this up
+ # sanely we need the order to be correct.
+ #
+ # For squeeze systems (no m-a), apt-cache policy output
+ # is all different.
$pkgname = $1;
+ $candidate_found = 0;
+ if ($has_arch) {
+ my $from_list = shift @installed_packages;
+ next if ($pkgname eq $from_list); # no :$arch in pkgname we asked for
+
+ my $ma_fix_pkgname = $pkgname.':'.$installed->{$from_list}->{'arch'};
+ my $ma_fix_from_list = $from_list.':'.$installed->{$from_list}->{'arch'};
+
+ if ($pkgname eq $ma_fix_from_list || # e.g. ia32-libs-i386. dpkg -l: ia32-libs-i386, apt-cache policy: ia32-libs-i386:i386
+ $ma_fix_pkgname eq $from_list) {
+ $pkgname = $from_list;
+ } else {
+ die "Unexpected order mismatch in apt-cache policy output (apt-cache policy name: $pkgname - dpkg -l name: $from_list)\n";
+ }
+ }
} elsif ($line =~ /^ +Installed: (.*)$/) {
# etch dpkg -l does not print epochs, so use this info, it's better
$installed->{$pkgname}{'installed'} = $1;
+ # initialize security-update
+ $installed->{$pkgname}{'security-update'} = 0;
} elsif ($line =~ /^ +Candidate: (.*)$/) {
$installed->{$pkgname}{'candidate'} = $1;
+ } elsif ($line =~ / ([^ ]+) [0-9]+/) {
+ # check if the next lines show the sources of our candidate
+ if ($1 eq $installed->{$pkgname}{'candidate'}) {
+ $candidate_found = 1;
+ }
+ } elsif (($line =~ / +[0-9]+ [^ ]+\/(security\.([^ ]+\.)?debian\.org|debian-security).*\/updates\//) && $candidate_found ) {
+ $installed->{$pkgname}{'security-update'} = 1;
} elsif ($line =~ /^ +\*\*\*/) {
- my @l;
- @l = split(/ +/, $line);
$line = shift @lines;
- @l = split(/ +/, $line);
+ my @l = split(/ +/, $line);
$installed->{$pkgname}{'origin'} = $l[2];
+ $candidate_found = 0;
}
}
- my (%current, %obsolete, %outofdate);
+ my (%current, %obsolete, %outofdate, %security_outofdate);
for my $pkgname (keys %$installed) {
my $pkg = $installed->{$pkgname};
unless (defined($pkg->{'candidate'}) && defined($pkg->{'origin'})) {
- $obsolete{$pkgname} = $pkg;
- next;
- }
-
+ $obsolete{$pkgname} = $pkg;
+ next;
+ }
+
if ($pkg->{'candidate'} ne $pkg->{'installed'}) {
- $outofdate{$pkgname} = $pkg;
+ if ($pkg->{'security-update'}) {
+ $security_outofdate{$pkgname} = $pkg;
+ } else {
+ $outofdate{$pkgname} = $pkg;
+ }
next;
};
if ($pkg->{'origin'} eq '/var/lib/dpkg/status') {
$pkgs{'current'} = \%current;
$pkgs{'outofdate'} = \%outofdate;
+ $pkgs{'security_outofdate'} = \%security_outofdate;
$pkgs{'obsolete'} = \%obsolete;
return \%pkgs;
}
'short' => "%d pc",
'perf' => "prg_conf=%d;1;;0",
'status' => 'WARNING' },
+ { 'key' => 'security_outofdate',
+ 'listpackages' => 1,
+ 'long' => "%d packages with outstanding security updates: %s",
+ 'short' => "%d security-updates",
+ 'perf' => "security_outdated=%d;;1;0",
+ 'status' => 'CRITICAL' },
);
my @longout;
my $longout = join("\n", @longout);
my $perfout = "|".join(" ", @perfout);
-print $shortout;
+print $shortout,"\n";
print $longout,"\n";
print $perfout,"\n";