#!/usr/bin/perl -w

# Copyright (C) 2007 Christoph Berg <myon@debian.org>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.

use strict;
use IPC::Open2;

$ENV{'PATH'} = '/bin:/sbin:/usr/bin:/usr/sbin';
$ENV{'LC_ALL'} = 'C';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

my %packages;

open P, "dpkg --get-selections |" or die "dpkg: $!";
while (<P>) {
	if (/^(\S+)\s+(\S+)/) {
		next unless $2 eq "install" or $2 eq "hold";
		$packages{$1} = $2;
	}
}
close P;

my $pid = open2(\*P, \*IN, qw/xargs -r apt-cache policy/);
die "open2: $!" unless $pid;

if (!fork()) {
	close P;
	foreach my $p (sort keys %packages) {
		print IN "$p\n";
	}
	close IN;
	exit 0;
}
close IN;

my ($pkg, $inst, $cand, $in_dist, $dist);
my (@up, @upgrades, @sec, @security, @holdup, @holdupgrades, @holdsec, @holdsecurity);

sub try_pkg ()
{
	return if $inst eq $cand;
	#print "$pkg $inst $cand ($dist)\n";
	if ($packages{$pkg} eq "hold") {
		if ($dist and $dist =~ /updates/) {
			push @holdsec, $pkg;
			push @holdsecurity, "$pkg ($inst $cand)";
		} else {
			push @holdup, $pkg;
			push @holdupgrades, "$pkg ($inst $cand)";
		}
		return;
	}
	if ($dist and $dist =~ /updates/) {
		push @sec, $pkg;
		push @security, "$pkg ($inst $cand)";
	} else {
		push @up, $pkg;
		push @upgrades, "$pkg ($inst $cand)";
	}
}

while (<P>) {
	if (/^(\S+):/) {
		my $next_pkg = $1;
		try_pkg () if $pkg;
		$pkg = $next_pkg;
		undef $dist;
	}
	$inst = $1 if / +Installed: (.+)/;
	$cand = $1 if / +Candidate: (.+)/;
	if (/^[ *]+(\S+) 0$/) {
		$in_dist = ($1 eq $cand);
	}
	if ($in_dist and /^ +\d+ \S+ (\S+)/) { # 700 http://localhost lenny/main Packages
		$dist .= "$1 ";
	}
}
try_pkg ();
close P;

waitpid $pid, 0;

my $last_update = -M "/var/lib/apt/lists/lock";

my $color;
if (@security or $last_update >= 7) {
	$color = 'red';
} elsif (@upgrades or $last_update >= 1.5) {
	$color = 'yellow';
} else {
	$color = 'green';
}

my $date = scalar localtime;

open BB, "| $ENV{BB} $ENV{BBDISP} @" or die;
print BB "status $ENV{CLIENTHOSTNAME}.apt $color $date\n\n";
print BB "Security updates: ";
if (@security) {
	print BB "apt-get install ", join (" ", @sec), "\n";
	print BB map { "  $_\n" } @security;
} else {
	print BB "none\n";
}
print BB "\nOther updates: ";
if (@upgrades) {
	print BB "apt-get install ", join (" ", @up), "\n";
	print BB map { "  $_\n" } @upgrades;
} else {
	print BB "none\n";
}
if (@holdsecurity) {
	print BB "\nSecurity updates on hold: ";
	print BB "apt-get install ", join (" ", @holdsec), "\n";
	print BB map { "  $_\n" } @holdsecurity;
}
if (@holdupgrades) {
	print BB "\nOther updates on hold: ";
	print BB "apt-get install ", join (" ", @holdup), "\n";
	print BB map { "  $_\n" } @holdupgrades;
}
printf BB "\nLast apt update: %.1f day(s) ago\n", $last_update;
close BB;

exit 0;
