#!/usr/bin/perl -w

# Copyright (C) 2005, 2006, 2007, 2008 Peter Palfrader <peter@palfrader.org>
# Porting to hobbit Copyright (C) 2007 Christoph Berg <myon@debian.org>
# Copyright (C) 2011 Axel Beckert <abe@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 English;
use Hobbit;

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

my $LSOF = '/usr/bin/lsof';
my $SUDO = '/usr/bin/sudo';

my $bb = new Hobbit ('libs');

################### Kernel

my $current = `uname -r`;
chomp $current;

my $installed = readlink "/vmlinuz";
$installed =~ s!.*vmlinuz-!! if $installed;

if ($installed and ($current ne $installed)) {
	$bb->color_line ('yellow', "Machine should be rebooted, newer kernel is installed: $current $installed\n\n");
}

my $curconfig = "/proc/config.gz";
my $instconfig = "/boot/config-$current";

if (-e $curconfig and -e $instconfig) {
	system "zdiff -q $curconfig $instconfig > /dev/null";
	if ($? >> 8 != 0) {
		$bb->color_line ('yellow', "Machine should be rebooted, kernel configs $curconfig and $instconfig differ\n\n");
	}
} else {
	$bb->color_line ('clear', "One of $curconfig and $instconfig was not found, cannot check kernel uptodateness\n\n");
}

###################

my %processes;

sub getPIDs($$) {
	my ($user, $process) = @_;
	return join(' ', sort keys %{ $processes{$user}->{$process} });
};
sub getProcs($) {
	my ($user) = @_;

	return join("\n", map { "  $_ (".getPIDs($user, $_).')' } (sort {$a cmp $b} keys %{ $processes{$user} }));
};
sub getUsers() {
	return join("\n", (map { "$_:\n".getProcs($_) } (sort {$a cmp $b} keys %processes)));
};
sub inVserver() {
	my ($f, $key);
	if (-e "/proc/self/vinfo" ) {
		$f = "/proc/self/vinfo";
		$key = "XID";
	} else {
		$f = "/proc/self/status";
		$key = "s_context";
	};
	open(F, '<', $f) or return 0;
	while (<F>) {
		my ($k, $v) = split(/: */, $_, 2);
		if ($k eq $key) {
			close F;
			return ($v > 0);
		};
	};
	close F;
	return 0;
}

my $INVSERVER = inVserver();

if (!-x $SUDO or !-x $LSOF) {
    $bb->color_line ('clear',
"$SUDO and/or $LSOF not found or not executable.

This library check needs lsof to be run with root rights. See the file
/etc/sudoers.d/xymon for how lsof would be run with root rights via
sudo.

");

    $bb->send;
    exit 0
}

if (!-e '/etc/sudoers.d/README') {
    $bb->color_line ('yellow',

"sudo seems to be installed, but an older version without support for files in /etc/sudoers.d/.

Please add the contents of /etc/sudoers.d/xymon to /etc/sudoers
and create /etc/sudoers.d/README to disable this warning:

  cat /etc/sudoers.d/xymon >> /etc/sudoers
  echo See /usr/lib/xymon/client/ext/libs >> /etc/sudoers.d/README

");

}

unless (open(LSOF, '-|', "$SUDO $LSOF -n 2> /dev/null")) {
	$bb->color_line ('red', "Cannot run $LSOF -n: $!\n");
	$bb->send;
	exit;
}
my @lsof=<LSOF>;
close LSOF;
if ($CHILD_ERROR) { # program failed
	$bb->color_line ('red', "$LSOF -n returned with non-zero exit code: ".($CHILD_ERROR / 256)."\n");
	$bb->send;
	exit;
};

for my $line (@lsof)  {
	my ($process, $pid, $user, $fd, undef, undef, undef, $path, $rest) = split /\s+/, $line;
	if ($line =~ m/\.dpkg-/ || $line =~ m/path inode=/ || $fd eq 'DEL') {
		next if $path =~ m#^/proc/#;
		next if $path =~ m#^/var/tmp/#;
		next if $path =~ m#^/SYS#;
		next if $path =~ m#^/drm$#; # xserver stuff
		next if $path =~ m#^/dev/zero#;
		next if ($INVSERVER && ($process eq 'init') && ($pid == 1) && ($user eq 'root'));
		#$processes{$user}->{$process} = [] unless defined $processes{$user}->{$process};
		$processes{$user}->{$process}->{$pid} = 1;
	};
};


my $libmsg;
if (keys %processes) {
	$bb->color_line ('yellow', "The following processes have libs linked that were upgraded:\n\n". getUsers() . "\n");
} else {
	$bb->color_line ('green', "No upgraded libs linked in running processes\n");
};

$bb->add_color ('green');
$bb->send;
