3 # Copyright (C) 2005, 2006, 2007, 2008, 2012, 2015 Peter Palfrader <peter@palfrader.org>
4 # 2012 Uli Martens <uli@youam.net>
6 # Permission is hereby granted, free of charge, to any person obtaining
7 # a copy of this software and associated documentation files (the
8 # "Software"), to deal in the Software without restriction, including
9 # without limitation the rights to use, copy, modify, merge, publish,
10 # distribute, sublicense, and/or sell copies of the Software, and to
11 # permit persons to whom the Software is furnished to do so, subject to
12 # the following conditions:
14 # The above copyright notice and this permission notice shall be
15 # included in all copies or substantial portions of the Software.
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 $ENV{'PATH'} = '/bin:/sbin:/usr/bin:/usr/sbin';
30 delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
32 my $LSOF = '/usr/bin/lsof -F0';
33 my $VERSION = '0.2015012901';
44 Getopt::Long::config('bundling');
52 '--help' => \$params->{'help'},
53 '--version' => \$params->{'version'},
54 '--quiet' => \$params->{'quiet'},
55 '--ignore-pid-namespaces' => \$params->{'ignore_pid_namespaces'},
56 '--verbose' => \$params->{'verbose'},
57 '-v' => \$params->{'verbose'},
58 '--config=s' => \$params->{'config'},
60 dief ("$PROGRAM_NAME: Usage: $PROGRAM_NAME [--help|--version] [--ignore-pid-namespaces] [--verbose] [--quiet] [--config=<CONFIGFILE>]\n");
62 if ($params->{'help'}) {
63 print "$PROGRAM_NAME: Usage: $PROGRAM_NAME [--help|--version] [--ignore-pid-namespaces] [--verbose] [--quiet] [--config=<CONFIGFILE>]\n";
64 print "Reports processes that are linked against libraries that no longer exist.\n";
65 print "The optional config file can specify ignore rules - see the sample config file.\n";
66 print "Can optionally ignore processes from other PID namespaces.\n";
69 if ($params->{'version'}) {
70 print "nagios-check-libs $VERSION\n";
71 print "nagios check for availability of debian (security) updates\n";
72 print "Copyright (c) 2005, 2006, 2007, 2008, 2012 Peter Palfrader <peter\@palfrader.org>\n";
76 if (! defined $params->{'config'}) {
77 $params->{'config'} = '/etc/nagios/check-libs.conf';
78 } elsif (! -e $params->{'config'}) {
79 dief("Config file $params->{'config'} does not exist.\n");
82 if (-e $params->{'config'}) {
83 eval "use YAML::Syck; 1" or dief "you need YAML::Syck (libyaml-syck-perl) to load a config file";
84 open(my $fh, '<', $params->{'config'}) or dief "Cannot open config file $params->{'config'}: $!";
85 $config = LoadFile($fh);
87 if (!(ref($config) eq "HASH")) {
88 dief("Loaded config is not a hash!\n");
93 '$path =~ m#^/proc/#',
94 '$path =~ m#^/var/tmp/#',
96 '$path =~ m#^/drm$# # xserver stuff',
97 '$path =~ m#^/dev/zero#',
98 '$path =~ m#^/dev/shm/#',
103 if (! exists $config->{'ignorelist'}) {
104 $config->{'ignorelist'} = [];
105 } elsif (! (ref($config->{'ignorelist'}) eq 'ARRAY')) {
106 dief("Config->ignorelist is not an array!\n");
113 my ($user, $process) = @_;
114 return join(', ', sort keys %{ $processes{$user}->{$process} });
119 return join(', ', map { $_.' ('.getPIDs($user, $_).')' } (sort {$a cmp $b} keys %{ $processes{$user} }));
122 return join('; ', (map { $_.': '.getProcs($_) } (sort {$a cmp $b} keys %processes)));
126 if (-e "/proc/self/vinfo" ) {
127 $f = "/proc/self/vinfo";
130 $f = "/proc/self/status";
133 open(F, "< $f") or return 0;
135 my ($k, $v) = split(/: */, $_, 2);
145 my $INVSERVER = inVserver();
147 my $PID_NS_SUPPORT = (-f '/proc/self/ns/pid');
149 if ($PID_NS_SUPPORT and $params->{'ignore_pid_namespaces'}) {
150 $PID_NS = readlink('/proc/self/ns/pid');
153 print STDERR "Running $LSOF -n\n" if $params->{'verbose'};
154 open (LSOF, "$LSOF -n 2>/dev/null|") or dief ("Cannot run $LSOF -n: $!\n");
157 if ($CHILD_ERROR) { # program failed
158 dief("$LSOF -n returned with non-zero exit code: ".($CHILD_ERROR / 256)."\n");
161 my ($process, $pid, $user);
162 LINE: for my $line (@lsof) {
163 if ( $line =~ /^p/ ) {
164 my %fields = map { m/^(.)(.*)$/ ; $1 => $2 } grep { defined $_ and length $_ >1} split /\0/, $line;
165 $process = $fields{c};
167 $user = $fields{L} || '<unknown>';
171 if ($PID_NS_SUPPORT and $params->{'ignore_pid_namespaces'}) {
172 my $pidns = readlink('/proc/'.$pid.'/ns/pid');
173 next if (defined $pidns and $PID_NS ne $pidns);
176 unless ( $line =~ /^f/ ) {
177 dief("UNKNOWN strange line read from lsof\n");
178 # don't print it because it contains NULL characters...
181 my %fields = map { m/^(.)(.*)$/ ; $1 => $2 } grep { defined $_ and length $_ >1} split /\0/, $line;
184 my $inode = $fields{i};
185 my $path = $fields{n};
186 if ($path =~ m/\.dpkg-/ || $path =~ m/\(deleted\)/ || $path =~ /path inode=/ || $path =~ m#/\.nfs# || $fd eq 'DEL') {
187 my $deleted_in_path = ($path =~ m/\(deleted\)/ || $path =~ m/\.nfs/);
188 next if ($deleted_in_path && $fd =~ /^[0-9]*$/); # Ignore deleted files that are open via normal file handles.
189 next if ($deleted_in_path && $fd eq 'cwd'); # Ignore deleted directories that we happen to be in.
191 $path =~ s/^\(deleted\)//; # in some cases "(deleted)" is at the beginning of the string
192 for my $i (@{$config->{'ignorelist'}}) {
193 my $ignore = eval($i);
194 next LINE if $ignore;
196 next if ($INVSERVER && ($process eq 'init') && ($pid == 1) && ($user eq 'root'));
197 if ( $params->{'verbose'} ) {
198 print STDERR "adding $process($pid) because of [$path]:\n";
201 $processes{$user}->{$process}->{$pid} = 1;
209 if (keys %processes) {
211 $message = 'The following processes have libs linked that were upgraded: '. getUsers()."\n";
213 $message = "No upgraded libs linked in running processes\n" unless $params->{'quiet'};