#!/usr/bin/perl ## This Script is free for all. Please feel free to distribute it to anybody! I do not warantee this program ## in any way or for any reason, if it blows up and ruins your entire business database or anything, I accept no responsibility! ## Please email abhidharnoida@yahoo.com with any problems or comments. ## NOTE: This is meant to run on linux with perl on it. It should also have the module "Net::SSH::Perl" installed on it. ##Please install Net::SSH::Perl from CPAN ##using can be easily done by executing the following command. ## perl -MCPAN -e 'install Net::SSH::Perl' ## ##usage:: proclist --hosts [ip1],[ip2],[ip2] ... --search search-string --kill [signo] ##WARNING!!! USE THE KILL OPTION WITH CARE. use Getopt::Long; #use Net::SSH::Perl; #use Term::ReadKey; my $VERSION = '1.1'; GetOptions ("hosts=s" => \$H, "search=s" => \$S, "kill=s" => \$K); my @hosts =split(/,/,$H) ; if (@hosts == "") { #Case 1: when then script is run on the machine itself. #find username and uids, Puts them is the usr hash, %usr=(); open(PWD,"/etc/passwd") || die; while (){ @P= split(/:/,$_); $usr{$P[2]}=$P[0]; } #scans proc for sockets, fds and pids opendir (PROC, "/proc") || die "proc"; for $f (readdir(PROC)){ next if (! ($f=~/[0-9]+/) ); if (! opendir (PORTS, "/proc/$f/fd")) { # print "failed opendir on process $f fds\n"; closedir PORTS; next; } for $g (readdir(PORTS)) { next if (! ($g=~/[0-9]+/) ); $r=readlink("/proc/$f/fd/$g"); ($dev,$ino)=($r=~/^(socket|\[[0-9a-fA-F]+\]):\[?([0-9]+)\]?$/); if ($dev == "[0000]" || $dev == "socket") { $sock_proc{$ino}=$f.":".$g if $ino != "" ;} } closedir PORTS; } closedir PROC; #for $a (keys(%sock_proc)) {print "$a->$sock_proc{$a}\n";} #header print "type port inode user pid fd name local_address rem_address\n"; #displays output. scheck("tcp"); scheck("udp"); scheck("raw"); print ("\nThe following process are killed using: kill -".$K."\n") if $K ne "" ; } else { #Case2: to be executed on multiple servers. require Net::SSH::Perl || die "Please load Net::SSH::Perl from CPAN.org"; require Term::ReadKey || die "Please load Term::ReadKey from CPAN.org"; @passwd =(); #collect password for hosts for (@hosts){ print "Enter root password ".$_.":"; Term::ReadKey::ReadMode('noecho'); chomp(my $pwd = Term::ReadKey::ReadLine(0)); Term::ReadKey::ReadMode('restore'); print "\n"; push @passwd, $pwd; } for my $i (0..$#hosts) { #ssh every hostname; my $ssh = Net::SSH::Perl->new($hosts[$i]); $ssh->login("root", $passwd[$i]); #change to proclist my $c = "/usr/bin/procsocklist"; $c= $c." --search ".$S if $S ne ""; $c= $c." --kill ".$K if $K ne ""; #execute the script. my($stdout, $stderr, $exit); eval{ ($stdout, $stderr, $exit) = $ssh->cmd($c)}; if ($@){ print "ERROR:Wrong password for $hosts[$i]\n" ;} #display data from host else { print "#################################$hosts[$i]##################################\n"; print $stdout; print "ERROR:",$stderr if $stderr; print "EXIT:",$exit if $exit; } } } exit(0); sub scheck { #reads the /proc/net/(tcp|udp|raw) files and extracts inode and other information. #with the help of $sock_proc and %usr it associates pids and usernames. open(FILE,"/proc/net/".$_[0]) || die; while () { @F=split(); next if ($F[9]=~/uid/); @A=split(":",$F[1]); $a=hex($A[1]); $local_address = converthex2ip($F[1]); $rem_address = converthex2ip($F[2]); if (exists $sock_proc{$F[9]}) { ($pid,$fd)=($sock_proc{$F[9]}=~m.([0-9]*):([0-9]*).) ; } $cmd = ""; if ($pid && open (CMD,"/proc/$pid/status")) { $l = ; ($cmd) = ( $l=~/Name:\s*(\S+)/ ); close(CMD); } $display = sprintf ("%-3s %6d %10d %-10.10s %6d %4d %-10.10s %-20.20s %-20.20s\n",$_[0], $a ,$F[9], $usr{$F[7]}, $pid, $fd, $cmd,$local_address,$rem_address); #search option #the regex is executed on the row displayed. I think this will make things flexible. if ($S ne "") { if ($display =~ /$S/) { print $display ; #kill option on the filtered Items only. system ("kill -".$K." ".$pid) if $K ne "" ; } } else {print $display ;} } close(FILE); } #converts hex format of ips used in /proc/net/(tcp|udp|raw) files to dot-format which people can understand. sub converthex2ip{ my $addr=shift @_; my $pattern = "([0-9A-Z]{2})([0-9A-Z]{2})([0-9A-Z]{2})([0-9A-Z]{2})" .":([0-9A-Z]{2})([0-9A-Z]{2})"; if ($addr =~ m/^$pattern$/) { my @octets_hex=($4,$3,$2,$1); my @port_hex=($5,$6); my @octets=map { hex($_) } @octets_hex; my $dot_ip=join('.', @octets); my $port = hex(join('', @port_hex)); return "$dot_ip:$port"; } return undef; } =head1 NAME Procsocklist.pl 1.0 - Displays users of different servers with there pids and associated sockects to those pids. =head1 Author Abhinav Dhar, abhidharnoida@yahoo.com =head1 DESCRIPTION usage:: proclist --hosts [ip1],[ip2],[ip2] ... --search search-string --kill [sig] procsocklist helps the user to collect information about sockets related to different ips or programs,etc. Like if a one wants to find users connected to a perticular port or ip in a given number of hosts machines then it can be easily done with the help of this script. We can filter our search and then perform a kill on the listed result (ie with the help of kill option). WARNING: please view the result without the kill option first. Once you are sure that you want to perform a kill on all these process then add a kill option followed by signal number (say 9). --hosts : the ips should by separated by commas only. --search : regex to match the each output row. --kill : will perform a kill with the signal number specified on the pids that are listed. The output is like this: type port inode user pid fd name local_address rem_address tcp 139 137988 root 19706 9 smbd 0.0.0.0:139 0.0.0.0:0 tcp 139 138176 root 19707 12 smbd 192.168.6.20:139 192.168.6.137:1791 udp 32773 138177 root 19707 5 smbd 127.0.0.1:32773 0.0.0.0:0 type - socket type tcp, udp or raw port - port number. inode - is inode. local_address- The local IP address and port number for the socket. rem_address- The remote IP address and port number for the socket. name - program name. Actually this script will be helpful in many cases. Like suppose users are accessing a database on a particular IP through various programs like apache or sqlplus. This access has to be stopped by the sys-admin. This script will be helpful for him in managing processes trying to connect to that particular IP. =head1 PREREQUISITES This script requires the C & C modules in the case of using host arguments. It also requires C. Apart from that the user *must* have root rights to all hosts. One more *IMPORTANT* thing procsocklist.pl should be in /usr/bin/procsocklist [note no .pl extension] in every host machine so that it can run as a command. =pod OSNAMES Linux =pod SCRIPT CATEGORIES Networking =cut