# This is a simple wrapper for SNMP_utils. People who want to # use other SNMP libraries can hook the calls here by replacing this # copy of snmpUtils.pm with their own, which redirects the calls # to their own library. # Specify the name of this new package. package clientUtils; use Common::Log; use BER; use SNMP_Session; use SNMP_util; use Sys::Hostname; use IO::Socket; # snmp_utils 0.55 has a reference to main::DEBUG, so we instantiate # it here, if necessary if (! defined($main::DEBUG)) { $main::DEBUG = 0; } # this keeps BER from formatting timeticks, so that if someone # want to put them into an RRD, it would work. $BER::pretty_print_timeticks = 0; my($err) = ''; # this funky wrapper is because SNMP_Session throws exceptions with # warn(), which we need to catch, instead of letting them run # roughshod over Cricket's output. sub _do { my($subref, @args) = @_; my(@res); $err = ''; eval { local($SIG{'__WARN__'}) = sub { $err = $_[0]; die($err); }; @res = &{$subref}(@args); }; # do a little post processing on the overly wordy errors # that SNMP_Session gives us... if (defined($err) && $err ne '') { my(@err) = split(/\n\s*/, $err); if ($#err+1 > 2) { my($code) = (split(/: /, $err[2]))[1]; $err = "$err[1] $code."; if ($code eq "noSuchName") { my($oid) = $err[3]; $oid =~ s/.*\((.*)\).*/$1/; $err .= " $oid"; } } else { $err =~ s/\n//g; } Warn($err); } return @res; } sub get { _do(\&clientget, @_); } sub getnext { _do(\&snmpgetnext, @_); } sub walk { _do(\&snmpwalk, @_); } sub trap2 { my($to, $spec, @data) = @_; # this is the OID for enterprises.webtv.wtvOps.wtvOpsTraps my($ent) = ".1.3.6.1.4.1.2595.1.1"; _do(\&snmptrap, $to, $ent, hostname(), 6, $spec, @data); } sub trap { my($to, $spec, @data) = @_; # this is the OID for enterprises.webtv.wtvOps.wtvOpsTraps my($ent) = ".1.3.6.1.4.1.2595.1.1"; # this makes a oid->value map for the trap. Note that # we just fake up simple one-level OID's... it suits our needs. my($item, @vars); my($ct) = 1; foreach $item (@data) { my($type) = "string"; $type = "integer" if ($item =~ /^(\d+)$/); push @vars, $ct, $type, $item; $ct++; } _do(\&snmptrap, $to, $ent, hostname(), 6, $spec, @vars); } # This is a pretty basic Perl client subroutine. # It takes arguments from the collector, connects # to the appropriate server (on the target host) # and sends the arguments to the server. It then # waits for results from the server and reports # them back to the collector. It follows the # cricket convention where all of the data to be # retrieved is sent in one message instead of # multiple messages. # This is a pretty ugly hack, but it was the only # way I could figure for getting non-standard data # off our hosts. Turns out it is pretty flexible. sub clientget { my (@args) = @_; my ($data) = ""; my ($host_info) = ""; my (@host_info) = ""; my ($hostname) = ""; my ($port) = ""; my ($passwd) = ""; my ($sock) = ""; my ($i) = ""; my ($timeout) = 10; $host_info = $args[0]; for ( $i = 1; $i <= $#args; $i++) { $data = "$data $args[$i]"; } Debug("clientget: args = @args"); Debug("data = $data"); @host_info = split(/@/, $host_info); $passwd = $host_info[0]; $host_info = $host_info[1]; @host_info = split(/:/, $host_info); $hostname = $host_info[0]; $port = $host_info[1]; Debug("hostname:$hostname, passwd: $passwd, port: $port"); $sock = new IO::Socket::INET (PeerHost => $hostname, PeerPort => $port, Proto => 'tcp', Timeout => $timeout, ); Debug("Socket could not be created: $!") unless $sock; return 0 unless $sock; Debug("Sending login/password: $passwd"); print $sock "login:$passwd\n"; if (defined ($buf = <$sock>)) { chomp($buf); Debug("server answer: $buf"); } if ($buf eq "password accepted") { Debug("Sending data: $data"); print $sock "get_info:$data\n"; } if (defined ($buf = <$sock>)) { chomp($buf); Debug("server answer: $buf"); } Debug("Sending logout"); print $sock "logout\n"; # Change return into array @buf = split(/,/, $buf); close ($sock); return (@buf); } 1;