#!/usr/bin/perl ################################################################################ # # # Version 2.15 by Nick Reinking, original code by Charlie Cook. # # See LICENSE for copyright information # # # # netsaint_statd [port] # # # # netsaint_statd is a daemon which allows for a Netsaint host using # # scripts to get information suck as process count, users, disk usage, and # # load information. This daemon does not process the information in anyway. # # It merely collects the info and hands it back to the calling script to do # # with as it pleases. # # # # This script is designed in such a way as to allow for easy porting via the # # %commandlist hash. Adding other checks should also be as easy as making a # # minor modification to the %commandlist hash for the wanted command. # # # # Function calls have been recently reworked. This is to allow for *much* # # easier extensions of netsaint_statd. When a client communicates to # # netsaint_statd, it sends one line, which contains a request for information # # on a particular resource, and arguments for that request. The new # # netsaint_statd will call the function by the resource called, and pass it # # the arguments. For example, client A connections to server B, and sends the # # line "dog /with/one/tail". netsaint_statd will then call the function dog() # # and pass it the parameter /with/one/tail. This should make adding extra # # functionality as easy adding a single function, and modifying an existing # # client script. Note that you must enter these functions in the dispatch # # table below. If you need to call commands from the %commandlist for your # # functions, they are referenced by $commandlist{$os}{functionname}. So, if # # you want the df command for your current machine, you can get it by # # accessing $commandlist{$os}{dfcommand}. It's that easy. Sending the client # # information is a simple 'print Client information'. # # # ################################################################################ # Uncomment @allowed_hosts if you want to restrict access to only # certain hosts. Leaving it uncommented allows everybody to access # netsaint_statd. I recommend placing the IP of your netsaint # server in here, for security reasons (not that my program has any # security bugs). :P my @allowed_hosts; # = ("10.55.0.18"); my $os = os_uname(); $os = "IRIX" if $os =~ "IRIX64"; # IRIX can have two different unames. # my $os = "NEXTSTEP"; # Uncomment this if you have a NeXT machine. my %commandlist = ( "HP-UX" => { dfcommand => "/bin/bdf -l", whocommand => "/bin/who -q | /usr/bin/grep \"#\"", proccommand => "/bin/ps -el", procstates => "SWRITZX", uptimecommand => "/bin/uptime", }, "Linux" => { dfcommand => "/bin/df -k", whocommand => "/usr/bin/who -q | /bin/grep \"#\"", proccommand => "/bin/ps ax", procstates => "RSTDZ", uptimecommand => "/usr/bin/uptime", }, "SunOS" => { dfcommand => "/bin/df -k", # dfcommand => "/bin/df -kl", # For only local disks whocommand => "/bin/who -q | /usr/bin/grep \"#\"", proccommand => "ps -e -o \"pid tty s time comm\"", procstates => "SRZTO", uptimecommand => "/usr/bin/uptime", }, "IRIX" => { dfcommand => "/bin/df -kP", # dfcommand => "/bin/df -klP", # For only local disks whocommand => "/bin/who -q | /usr/bin/grep \"#\"", proccommand => "ps -e -o \"pid tty state time comm\"", procstates => "SRZTIXC0", uptimecommand => "/usr/bsd/uptime", }, "OSF1" => { dfcommand => "/bin/df -k", whocommand => "/bin/who -q | /usr/bin/grep \"Total user\"", proccommand => "ps -e -o \"pid tty state time comm\"", procstates => "RVSITH", uptimecommand => "/bin/uptime", }, "FreeBSD" => { dfcommand => "/bin/df -k", whocommand => "/usr/bin/who | /usr/bin/wc -l", proccommand => "/bin/ps ax", procstates => "RSTDIZ", uptimecommand => "/usr/bin/uptime", raid3wareunits => "/usr/local/sbin/tw_cli info XXX unitstatus", raid3ware => "/usr/local/sbin/tw_cli info", }, "NEXTSTEP" => { dfcommand => "/bin/df", whocommand => "/bin/who | /usr/ucb/wc -l", proccommand => "/bin/ps -ax", procstates => "RSTDIZ", uptimecommand => "/usr/bin/uptime", }, "BSD/OS" => { dfcommand => "/bin/df", whocommand => "/usr/bin/who | /usr/bin/wc -l", proccommand => "/bin/ps -ax", procstates => "RSTDIZ", uptimecommand => "/usr/bin/uptime", }, "OpenBSD" => { dfcommand => "/bin/df -k", whocommand => "/usr/bin/who | /usr/bin/wc -l", proccommand => "/bin/ps -ax", procstates => "RIDLIS", uptimecommand => "/usr/bin/uptime", }, "AIX" => { dfcommand => "/usr/bin/df -Ik", whocommand => "/usr/bin/who | /usr/bin/wc -l", proccommand => "/usr/bin/ps x", procstates => "OAWISTZ", uptimecommand => "/usr/bin/uptime", }, "NetBSD" => { dfcommand => "/usr/bin/df -k", whocommand => "/usr/bin/who | /usr/bin/wc -l", proccommand => "/bin/ps ax", procstates => "RSTDIZ", uptimecommand => "/usr/bin/uptime", }, "UNIXWARE2" => { dfcommand => "/usr/ucb/df", whocommand => "/usr/bin/who -q | /bin/grep \"#\"", proccommand => "/usr/bin/ps -el | awk '{printf(\"%6d%9s%2s%5s %s\\n\",\$4,substr(\$0, 61, 8),\$2,substr(\$0,69,5),substr(\$0,75))}", procstates => "OSRIZTX", uptimecommand => "echo `/usr/bin/uptime`, load average: 0.00, `sar | awk '{oldidle=idle;idle=\$5} END {print 100-oldidle}'`,0.00", }, "SCO-SV" => { dfcommand => "/bin/df -Bk", whocommand => "/bin/who -q | /usr/bin/grep \"#\"", proccommand => "ps -el -o \"pid tty s time args\"", procstates => "OSRIZTB", uptimecommand => "/usr/bin/uptime", } ); # Dispatch table for subroutines my %subroutines = ( "users" => \&users, "disk" => \&disk, "alldisks" => \&alldisks, "uptime" => \&uptime, "procs" => \&procs, "named_proc" => \&named_proc, "raid3wareunits" => \&raid3wareunits, "raid3ware" => \&raid3ware ); ################################################################################ ################################################################################ require 5.003; BEGIN { $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin:/usr/sbin' } use POSIX; use Socket; use strict; # Setup upgly global varialbes my %restrictions; # Forking into a new daemon... my $pid = fork; exit if $pid; die "Couldn't fork: $!\n" unless defined($pid); POSIX::setsid() || die "Cannot spawn new session id: $!\n"; # Verifying IP restriction information... &verify_ip_list if @allowed_hosts; # Extra information needed... my $port = shift || 1040; chomp($os); # Setting up server for listening... my $proto = getprotobyname('tcp'); socket(Server, PF_INET, SOCK_STREAM, $proto) || die "Can't create socket: $!\n"; setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, 1) || die "Can't setsockopt $!\n"; bind(Server, sockaddr_in($port, INADDR_ANY)) || die "Can't bind to socket: $!\n"; listen(Server,SOMAXCONN) || die "Can't listen to socket: $1"; # Infinite loop listening for client connections... while (my $paddr = accept(Client,Server)) { if (@allowed_hosts) { my ($cport, $packed_ip) = sockaddr_in($paddr); my $dotted_quad = inet_ntoa($packed_ip); unless ($restrictions{$dotted_quad}) { send(Client,"Sorry, you ($dotted_quad) are not among the allowed hosts...\n",0); close(Client); next; } } my ($command,$arg_for_command,$input) = undef; next unless(defined($input = )); if ($input =~ /^(\w*)\s?([\w\/]*)/) { $command = $1; $arg_for_command = $2 if $2; $command =~ tr/A-Z/a-z/; } # Call function by name (if in dispatch table)... if (exists $subroutines{$command}) { my $rsub = $subroutines{$command}; &$rsub($arg_for_command); close(Client); } else { send(Client,"Unknown command\n",0); close(Client); } } sub verify_ip_list ################################################################################ # verify_ip_list scans the @allowed_hosts array, and double checks to make # # sure that you didn't put anything that isn't an IP address in there. # ################################################################################ { for (my $i=0;$i<=$#allowed_hosts;$i++) { if ($allowed_hosts[$i] =~ /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/) { $restrictions{$allowed_hosts[$i]} = 1; } else { print "Sorry, your allowed hosts list doesn't contain valid IPs.\n"; exit(0); } } } sub os_uname { my $uname = ( -e '/usr/bin/uname' ) ? '/usr/bin/uname' : '/bin/uname'; my $os = (`$uname 2>/dev/null`); chomp $os; return $os ? $os : undef; } sub users { open(WHOOUT,"$commandlist{$os}{whocommand} |") || die; $_ = ; $_ =~ /[users]*[=|:]?\s?(\d+)/; my $users = $1; print Client "$users "; close(WHOOUT); } sub disk { my $arg = shift; my ($disk, $avail, $capper, $mountpt); open(DFOUTPUT,"$commandlist{$os}{dfcommand} |") || die; $_ = ; DFCHECK: while($_ = ) { if (/^([\w\/\:\.\-\=]*)\s*\d*\s*\d*\s*(\d*)\s*(\d*)\%\s*([\w\/\-]*)/) { $disk = $1; $avail = $2; $capper = $3; $mountpt = $4; last DFCHECK if ($disk =~ /$arg/); } } if (($disk =~ $arg) && $mountpt) { $capper = 100 - $capper; print Client "$disk $avail $capper $mountpt "; } else { print Client "not found"; } ($disk,$avail,$capper,$mountpt) = undef; close(DFOUTPUT); } sub alldisks { my $disklisting; open(DFOUTPUT,"$commandlist{$os}{dfcommand} |") || die; $_ = ; while($_ = ) { if (/^[\w\/\:\.\-\=]*\s*\d*\s*\d*\s*\d*\s*(\d*)\%\s*([\w\/\-]*)/) { $disklisting .= "(".$2.",".$1.")"; } } if ($disklisting) { print Client $disklisting; } else { print Client "no disks?"; } close(DFOUTPUT); undef $disklisting; } sub uptime { open(UPTIMEOUTPUT,"$commandlist{$os}{uptimecommand} |"); $_ = ; $_ =~ /up\s*(.*),\s*\d*\s*user[s]?,\s*load average[s]?:\s*[0-9\.]*,\s*([0-9\.]*),/; print Client "$1 - $2 "; close(UPTIMEOUTPUT); } sub procs { my $procs = -1; my $procflags = shift || $commandlist{$os}{procstates}; open(PROCOUT, "$commandlist{$os}{proccommand} |") || die; $_ = ; if ($procflags) { while (defined($_ = )) { if ($_ =~ /\s*([$procflags])\w*\s*\d*/) { $procs++; } } } else { while () { $procs++; } } print Client "$procs $procflags "; ($procs, $procflags) = undef; close(PROCOUT); } sub named_proc { my $args = shift; open(PROCOUT, "$commandlist{$os}{proccommand} | grep -v grep | grep $args |") || die; @_ = ; print Client ++$#_; close(PROCOUT); } sub raid3wareunits { my $controller = shift; my $unitlisting; my $command = "$commandlist{$os}{raid3wareunits}"; $command =~ s/XXX/$controller/g; open(PROCOUT, "$command |") || die; $_ = ; while($_ = ) { if (/^(u\S+)\s+(\S+)\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*/) { $unitlisting .= '(' . $1 . ',' . $2 . ',' . $3 .')'; } } if (defined($unitlisting)) { print Client $unitlisting; } else { print Client "no units?"; } $unitlisting = undef; close(PROCOUT); } sub raid3ware { my $controller = shift; my $controllerlisting; my $command = "$commandlist{$os}{raid3ware}"; open(PROCOUT, "$command |") || die; $_ = ; while($_ = ) { # Ctl Model Ports Drives Units NotOpt RRate VRate BBU # ------------------------------------------------------------------------ # c0 9550SX-8LP 8 8 3 0 4 4 Testing if (/^(c\d+)\s+(\S+)\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*/) { $controllerlisting .= '(' . $1 . ',' . $2 . ',' . $3 . ',' . $4 . ',' . $5 . ',' . $6 . ',' . $7 . ',' . $8 . ',' . $9 . ')'; } } if (defined($controllerlisting)) { print Client $controllerlisting; } else { print Client "no controllers?"; } $controllerlisting = undef; close(PROCOUT); }