#!/usr/bin/perl # # Simple script that finds processes running with deleted executables # or deleted dependency libraries # # Copyright 2012 Ingvar Hagelund # This program is free software: You can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. See . # use strict; use Date::Parse; my %processmap; my %installtime; my %libs; my $x=0; my $t=1; my $verbose=0; $verbose++ if $ARGV[0] eq "-v"; print "Parsing the process table\n" if $verbose; open ( my $ps, "ps ax --no-headers -o pid,lstart|" ) or die "Unable to open a pipe from ps, $!"; while (<$ps>) { if ( /^\s*(\d+)\s+(\w+\s+\w+\s+\d+\s+\d+:\d+:\d+\s+\d\d\d\d)/ ) { my $pid=$1; my $lstart=$2; if ( my $exe=readlink "/proc/$pid/exe" ) { $exe =~ s/([^\0]+)\0.*/$1/; # prelink adds this sometimes $exe =~ s/(.+)\.\#.+/$1/; # and this on rhel5 sometimes $exe =~ s/(.+)\#.+/$1/; # and this $exe =~ s/(.+)\s+.*/$1/; # This script is not whitespace proof $lstart = str2time($lstart); $processmap{$pid}[$x]=$exe; $processmap{$pid}[$t]=$lstart; } #else { # print "Unable to readlink proc/$pid/exe\n"; #} } else { print "Unable to parse "; print; } } close $ps; print "Getting info from rpm" if $verbose; foreach my $pid (sort keys %processmap) { # First check the binary my $exe=$processmap{$pid}[$x]; if ( $exe =~ /^\s+$/ ) { print "We hit a bug; only whitespace in \$exe\n"; } elsif ( ! defined $installtime{$exe} ) { # No support for multiowned binaries yet, thus head -1 open ( my $rpm,"rpm -qf --qf \"%{INSTALLTIME}\\n\" $exe|" ) or die "Unable to open a pipe from rpm, $!"; print "." if $verbose; my $it=<$rpm>; close $rpm; chomp $it; $installtime{$exe}=$it; # While we're at it, cache the rest of that rpm, so we # don't need to do it later open (my $rpm,"rpm -qlf $exe |") or die "Unable to open a pipe from rpm, $!"; while (<$rpm>) { chomp; $installtime{"$_"}=$it; } close $rpm; } # Also check library dependencies. May take a lot of time on a slow system. open ( my $ldd, "ldd $exe |" ) or die "Unable to ldd $exe"; while (<$ldd>) { if ( /^\s+\S+ => (\S+) \(\w+\)/ ) { my $lib=$1; push @{ $libs{$exe} },$lib; unless ( defined $installtime{$lib} ){ open (my $rpm,"rpm -qf --qf \"%{INSTALLTIME}\\n\" $lib|" ) or die "Unable to open a pipe from rpm, $!"; print "." if $verbose; my $it=<$rpm>; chomp $it; $installtime{$lib}=$it; } } } close $ldd; } print " Done\n" if $verbose; my %warnings; for my $pid (sort keys %processmap ) { my $exe=$processmap{$pid}[$x]; if ( $processmap{$pid}[$t] < $installtime{$exe} ) { $warnings{$exe}{$pid}=$pid; } # Library dependcy check for my $lib ( @{ $libs{$exe} } ) { if ( $processmap{$pid}[$t] < $installtime{$lib} ) { # Abusing hashes to eliminate double touples :-) $warnings{$exe}{$pid}=1; } } } for my $exe ( keys %warnings ) { print "Warning: Needs restart: $exe, pids"; for my $pid ( keys %{ $warnings{$exe} } ) { print " $pid"; } print "\n"; }