Reading and Concat'ing lines until specific value found in Perl - perl

Morning SO,
I'm working on redesigning a Perl script designed by an external software vendor. It uses Perl, which I've never written in before. Basically all it does is read a line in, and then send a syslog packet to the a destination host containing that line. I need to modify it so Perl will keep reading and concat'ing until it reaches "". The problem is, it loops on the first line of a given file, and therefore never reaches the send stage. Any guidance on this? I went back and read the original script again, and it doesn't look to have a mechanism for iterating through each line.
Edit: OK - so apparently I've gone stupid and can't understand basic iterative logic. Fixed the looping problem, now to fix the syslog sending problem. It reads the data in correctly, but never executes the syslog send request, which implies it's not getting into the if statements.
Program should eventually enter here: if($lineRead eq $check){//do something;}
#!/usr/bin/perl -w
use strict;
use warnings;
use lib qw(.);
use lib qw(<removed>);
use Syslog;
use Time::HiRes qw( time sleep usleep );
use Getopt::Std;
# create log entries at a fixed rate (n per sec)
# Option defautls
my $me = $0;
$me =~ s|.*/||;
my %options = (
d => "127.0.0.1", # host
p => 514, # port
f => "readme.syslog", # filename
b => 0, # burst
v => 0, # verbose
t => 0, # tcp vs. udp
l => 0, # loop option
# u => "127.0.0.2", # new IP to send
);
my $theProto='tcp';
# Help
sub HELP_MESSAGE {
print <<EOF;
$me [-d <host>] [-p <port>] [-f filename] [-u <IP>] [-l] [-t] [-b] [-n NAME] [-v] <messages per second>
Options:
-d : destination syslog host (default 127.0.0.1)
-p : destination port (default 514)
-f : filename to read (default readme.syslog)
-b : burst the same message for 20% of the delay time
-t : use TCP instead of UDP for sending syslogs
-v : verbose, display lines read in from file
-n : use NAME for object name in syslog header
-l : loop indefinately
-u : use this IP as spoofed sender (default is NOT to send IP header)
EOF
}
getopts('vbtlu:d:p:n:f:', \%options);
unless (#ARGV) {
print STDERR "Need an event rate.\n";
HELP_MESSAGE;
exit 1;
}
my $nmsg = shift #ARGV;
if (!($nmsg =~ /^\d+$/)) {
print "Invalid number of messages per second.\n";
HELP_MESSAGE;
exit 1;
}
if ($options{t}) { $theProto='tcp'; }
my $syslog = new Syslog(
name => $options{n}, # prog name for syslog header
facility => 'local6',
priority => 'info',
loghost => $options{d},
port => $options{p},
proto => $theProto,
);
sub doitall() { # for purpose of infinate looping
open(F,$options{f}) or die("Unable to open file: $options{f}\n");
print STDERR "generating $nmsg messages per second to $options{d}:$options{p}\n";
print STDERR "Ctrl-c to stop\n";
# delay in milliseconds
my $delay = 1.0/$nmsg;
my $resolution = 0.2;
my $burst = $nmsg * $resolution;
my $check = "</Event>";
my $lineRead;
my $payload="a";
if ($options{b}) {
print "Sending $burst messages every ", int ($delay * 1000), "ms\n";
}
while (<F>) {
#print $lineRead;
if ($options{v}) {
print "Read in: $_\n";
}
$lineRead=$_;
if($lineRead eq $check){
$payload = join $payload, $lineRead;
if ($options{b}) {
for (my $i = 0 ; $i < $burst; $i++) {
if ($options{u}) { print $payload; $syslog->send($payload, host=> $options{u}); }
else { print $payload; $syslog->send($payload); }
}
} else {
if ($options{u}) { print $payload; $syslog->send($payload, host=> $options{u}); }
else { print $payload; $syslog->send($payload); }
}
if ($delay > 0) {
if ($options{v}) {
print "waiting for ", int($delay * 1000), "ms ...\n";
}
usleep (1000000*$delay);
}
$lineRead = "a";
$payload = "a";
}
else{
$payload = join $payload, $lineRead;
}
}
close(F);
} # end of the subroutine
if ($options{l}) {
while (1) { doitall(); }
} else { doitall(); }
exit 0;

Related

test for available data in filehandle

For some reason I am implementing some specific network protocol similar to STOMP in plain pure Perl.
The connection can be either a direct network socket, or an SSL tunnel provided by openssl s_client created by a call to open3 (no IO::Socket::SSL available on the host).
Depending on the dialog a request to the server may or may not have a response, or may have multiple responses. How can I test the file descriptors for the existence of data? Currently when no data is available, it waits until the defined timeout.
EDIT: I have probably a vocabulary issue between file handle vs. file descriptor to perform my research. I just found that eof() may help but cannot use it correctly yet.
While it is a bit complicated to provide an SCCCE, here is the interesting parts of the code:
# creation of a direct socket connection
sub connect_direct_socket {
my ($host, $port) = #_;
my $sock = new IO::Socket::INET(PeerAddr => $host,
PeerPort => $port,
Proto => 'tcp') or die "Can't connect to $host:$port\n";
$sock->autoflush(1);
say STDERR "* connected to $host port $port" if $args{verbose} || $args{debug};
return $sock, $sock, undef;
}
# for HTTPS, we are "cheating" by creating a tunnel with OpenSSL in s_client mode
my $tunnel_pid;
sub connect_ssl_tunnel {
my ($dest) = #_;
my ($host, $port);
$host = $dest->{host};
$port = $dest->{port};
my $cmd = "openssl s_client -connect ${host}:${port} -servername ${host} -quiet";# -quiet -verify_quiet -partial_chain';
$tunnel_pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $cmd);
say STDERR "* connected via OpenSSL to $host:$port" if $args{verbose} || $args{debug};
say STDERR "* command = $cmd" if $args{debug};
$SIG{CHLD} = sub {
print STDERR "* REAPER: status $? on ${tunnel_pid}\n" if waitpid($tunnel_pid, 0) > 0 && $args{debug};
};
return *CMD_IN, *CMD_OUT, *CMD_ERR;
}
# later
($OUT, $IN, $ERR) = connect_direct_socket($url->{host}, $url->{port});
# or
($OUT, $IN, $ERR) = connect_ssl_tunnel($url);
# then I am sending with a
print $OUT $request;
# and read the response with
my $selector = IO::Select->new();
$selector->add($IN);
FRAME:
while (my #ready = $selector->can_read($args{'max-wait'} || $def_max_wait)) {
last unless #ready;
foreach my $fh (#ready) {
if (fileno($fh) == fileno($IN)) {
my $buf_size = 1024 * 1024;
my $block = $fh->sysread(my $buf, $buf_size);
if($block){
if ($buf =~ s/^\n*([^\n].*?)\n\n//s){
# process data here
}
if ($buf =~ s/^(.*?)\000\n*//s ){
goto EOR;
# next FRAME;
} }
$selector->remove($fh) if eof($fh);
}
}
}
EOR:
EDIT 2 and epilogue
As a summary, depending in the protocol dialog
a request can have an expected response (for instance a CONNECT must return a CONNECTED)
a request to get the pending messages can return a single response, multiple responses at once (without intermediate request), or no response (and in this case the can_read() with no parameter of Ikegami is blocking, what I want to avoid).
Thanks to Ikegami I have changed my code as the following:
the timeout argument to can_read() is passed as an argument to the sub that is processing the responses
for initial connections I am passing a timeout of several seconds
when I expect instant responses I am passing a timeout of 1 second
in the process loop, after any correct response I replace the initial timeout by a 0.1 to not block if no more data is waiting in the filehandle
Here is my updated code:
sub process_stomp_response {
my $IN = shift;
my $timeout = shift;
my $resp = [];
my $buf; # allocate the buffer once and not in loop - thanks Ikegami!
my $buf_size = 1024 * 1024;
my $selector = IO::Select->new();
$selector->add($IN);
FRAME:
while (1){
my #ready = $selector->can_read($timeout);
last FRAME unless #ready; # empty array = timed-out
foreach my $fh (#ready) {
if (fileno($fh) == fileno($IN)) {
my $bytes = $fh->sysread($buf, $buf_size);
# if bytes undef -> error, if 0 -> eof, else number of read bytes
my %frame;
if (defined $bytes){
if($bytes){
if ($buf =~ s/^\n*([^\n].*?)\n\n//s){
# process frame headers here
# [...]
}
if ($buf =~ s/^(.*?)\000\n*//s ){
# process frame body here
# [...]
push #$resp, \%frame;
$timeout = 0.1; # for next read short timeout
next FRAME;
}
} else {
# EOF
$selector->remove($fh);
last FRAME;
}
} else {
# something is wrong
say STDERR "Error reading STOMP response: $!";
}
} else {
# what? not the given fh
}
}
}
return $resp;
}
Do not use eof in conjunction with select (which can_read wraps). It performs a buffered read, which breaks select.
select will mark a handle as ready for reading when it reaches EOF, and sysread returns zero on EOF. So all you need to do to detect EOF is to check for sysread returning zero.
Note that using a new buffer for every pass was a mistake sysread can easily return only part of a message. The following fixes this, and shows how to handle errors and EOF from sysread.
Globals:
my %clients_by_fd;
When you get a new connection:
$selector->add( $fh );
$clients_by_fd{ fileno( $fh ) } = {
buf => "",
# Any other info you want here.
};
Event loop:
while ( 1 ) {
my #ready = $selector->can_read();
for my $fh ( #ready ) {
my $client = $clients_by_fd{ fileno( $fh ) };
my $buf_ref = \$client->{ buf };
my $rv = sysread( $fh, $$buf_ref, 1024*1024, length( $$buf_ref ) );
if ( !$rv ) {
if ( defined( $rv ) ) {
# EOF
if ( length( $$buf_ref ) ) {
warn( "Error reading: Incomplete message\n" );
}
} else {
# Error
warn( "Error reading: $!\n" );
}
delete $clients_by_fd{ fileno( $fh ) };
$select->remove( $fh );
}
while ( $$buf_ref =~ s/^.*?\n\n//s ) {
process_message( $client, $& );
}
}
}

filtering packets from a specific ip using perl and Net::Pcap and Net::PcapUtils

I've been trying to write a script that filters packets out of a device and from a specific ip address over that device.
I want data to be like the output i get from wireshark when you select a specific device and you use the ip.src==xx.xx.xx.xx
my program so far is like this
#!/usr/bin/perl -w
my $interface='eth1';
sub process_pkt #Packet processing routine
{
my ($user_data,$header, $packet) = #_;
my $minipacket = substr($packet,0,54);
print ("\n## raw: ###\n");
print ($minipacket);
print ("\n==Byte# / Hex / Dec / Bin==\n");
for ($i=0;$i<55;$i++)
{
$hexval = unpack('H2',substr($packet,$i,1));
$decval = hex(unpack('H2',substr($packet,$i,1)));
printf ("%03s-%02s-%03s-%08b\n", $i, $hexval, $decval, $decval);
}
}
# ######################################################################
# Here we are invoking the NetPcap module and looping through forever.
Net::PcapUtils::loop(\&process_pkt,
SNAPLEN => 65536, #Size of data to get from packet
PROMISC => 1, #Put in promiscuous mode
FILTER => 'tcp', #only pass TCP packets
DEV => $interface, );
and I am getting output
now i want to filter out packets that are received on the eth1 device and from the soruce ip of xx.xx.xx.xx
can we use the filter option in Net::PcapUtils::loop to do that?
and then i want packets of data length xx
...
i tried going through the documentation in cpan.org
but all i find is the options available.. i couldn't find any examples..
can someone please help me out?
improvements:
can i use something like
FILTER => 'ip src xx.xx.xx.xx'
after the
FILTER => 'tcp'
line in the code?
and can i somehow include the data length of the packet so as to filter the packets of data length = 86?
Alternative program i am using to get the payload of the packet:
#!/usr/bin/perl -w
# #########################
#
use Net::PcapUtils;
use NetPacket::Ethernet qw(:strip);
use NetPacket::IP;
use NetPacket::TCP;
use NetPacket::IP qw(:strip);
my $interface= 'eth1';
my $snaplen= 65536;
my $filter='tcp';
my $promisc = 1;
my $timeout = 10000 ;
my $err;
sub process_pkt
{
my ($user_data,$header,$packet) = #_;
$ip= NetPacket::IP->decode(eth_strip($packet));
$tcp= NetPacket::TCP->decode($ip->{data});
$payload = $tcp->{data};
print ("payload: \n ".$payload." \n----end-----\n");
for($i=0;$i<55;$i++){
$hexval = unpack('H2',substr($payload,$i,1));
open (MYFILE, '>>perldata1.txt');
print MYFILE ($i." :hex: ". $hexval."\n");
close (MYFILE);
}
}
Net::PcapUtils::loop(\&process_pkt,
SNAPLEN => 65536,
PROMISC => 1,
FILTER => 'tcp',
FILTER => 'ip src 129.7.236.40',
DEV => $interface, );
but am still not able to figure out how to get the length of the data field. :(
Thanks.
#!/usr/bin/perl -w
# #########################
#
use Net::PcapUtils;
use NetPacket::Ethernet qw(:strip);
use NetPacket::IP;
use NetPacket::TCP;
use NetPacket::IP qw(:strip);
use strict;
use Data::Dumper;
#use warnings;
my $interface= 'eth1';
my $snaplen= 65536;
my $filter='tcp';
my $promisc = 1;
my $timeout = 10000 ;
my $err;
my #array;
sub process_pkt
{
my ($user_data,$header,$packet) = #_;
my $ip= NetPacket::IP->decode(eth_strip($packet));
my $tcp= NetPacket::TCP->decode($ip->{data});
my $payload = $tcp->{data};
if(length($payload)==32)
{
for(my $decode=0;$decode<32;$decode++)
{
$array[$decode] = unpack('H2',substr($payload,$decode,1));
}
my $length= scalar(#array);
open (MYFILE, '>doorstatus.tab');
if($array[22] eq '0c')
{
print MYFILE ( " Decision: Granted\n");
}
elsif($array[22] eq '04')
{
print MYFILE ("Decision: Denied\n");
}
elsif($array[22] eq '0d')
{
print MYFILE ("Decision: Locked\n");
}
else
{
print MYFILE ("Decision: unknown \n");
}
#print MYFILE ( " Data: \n".Dumper(\#array)." \n");
close (MYFILE);
}
}
Net::PcapUtils::loop(\&process_pkt,
SNAPLEN => 65536,
PROMISC => 1,
FILTER => 'tcp',
FILTER => 'ip src xx.xx.xx.xx',
DEV => $interface, );
The code filters the data coming from a specific source into an array and you can do any thing with it,

Term::ReadKey, non-blocking read in raw mode: Detect EOF?

When I pipe stuff into my program, it does not seem to get any character like 0x4 to indicate EOF.
$ echo "abc" | map 'cat'
saw a: \x61
saw b: \x62
saw c: \x63
saw
: \x0A
zzzbc
^C
I have to press Ctrl+C to quit, but I'm not really sure what the Ctrl+C is acting on. It's probably having the shell send a SIGINT to the pipeline? I don't know how pipelines work on that level.
Here is my program map:
#!/usr/bin/env perl
use strict;
use warnings;
use IO::Pty::Easy;
use Term::ReadKey;
use Encode;
$#ARGV % 2 and die "Odd number of args required.\n";
if ($#ARGV == -1) {
warn ("No args provided. A command must be specified.\n");
exit 1;
}
# be sure to enter the command as a string
my %mapping = #ARGV[-#ARGV..-2];
my $interactive = -t STDIN;
# my %mapping = #ARGV;
# my #mapkeys = keys %mapping;
# warn #mapkeys;
if ($interactive) {
print "Spawning command in pty: #ARGV\n"
# print "\nContinue? (y/n)";
# my $y_n;
# while (($y_n = <STDIN>) !~ /^(y|n)$/) {
# print '(y/n)';
# }
# exit if $y_n eq "n\n";
}
my $pty = IO::Pty::Easy->new();
my $spawnret = $pty->spawn("#ARGV")."\n";
print STDERR "Spawning has failed: #ARGV\n" if !$spawnret;
ReadMode 4;
END {
ReadMode 0; # Reset tty mode before exiting
}
my $i = undef;
my $j = 0;
{
local $| = 1;
while (1) {
myread();
# responsive to key input, and pty output may be behind by 50ms
my $key = ReadKey(0.05);
# last if !defined($key) || !$key;
if (defined($key)) {
my $code = ord($key); # this byte is...
if ($interactive and $code == 4) {
# User types Ctrl+D
print STDERR "Saw ^D from term, embarking on filicide with TERM signal\n";
$pty->kill("TERM", 0); # blocks till death of child
myread();
$pty->close();
last;
}
printf("saw %s: \\x%02X\n", $key, $code);
# echo translated input to pty
if ($key eq "a") {
$pty->write("zzz"); # print 'Saw "a", wrote "zzz" to pty';
} else {
$pty->write($key); # print "Wrote to pty: $key";
}
}
}
}
sub myread {
# read out pty's activity to echo to stdout
my $from_pty = $pty->read(0);
if (defined($from_pty)) {
if ($from_pty) {
# print "read from pty -->$from_pty<--\n";
print $from_pty;
} else {
if ($from_pty eq '') {
# empty means EOF means pty has exited, so I exit because my fate is sealed
print STDERR "Got back from pty EOF, quitting\n" if $interactive;
$pty->close();
last;
}
}
}
}
That would explain why it produced "zzzbc".
Now my question is how can I get map to be able to know about echo "abc" having reached the end of input? cf. echo "abc" | cat completes on its own. ReadKey does not seem to provide API for determining this situation.
Similarly I am not sure how to do the same to pass along the EOF to the child in the pty. I am thinking this might cause issues when a command is to write to a file or something, because EOF vs sending a kill signal is the difference between writing the file correctly and not exiting cleanly.
Try reading from STDIN and not that $pty object. The pipe you create via the shell passes the data to your STDIN file descriptor 0, which in perl is your handle .
The $pty, I assume that's your terminal. That's why the script just hangs (I guess).

Perl SNMP trap generator for scale testing?

I've hacked the script below together to let me generate traps to a test server. What I really need is something that will generate traps at a large scale so that I can check my tools on the receiving end to find out where the bottleneck is, such as UDP, Net::SNMP, Perl, etc.
I had hoped this script would let me generate something like 10k events/second but I am sadly mistaken.
Does anyone know if I can do this in Perl or have a suggestion of another way to do it?
#! /usr/bin/perl
use strict;
use warnings;
use Log::Fast;
use FindBin;
use Getopt::Long;
use File::Basename;
use Cwd qw(abs_path);
my $ROOT_DIR = abs_path("$FindBin::Bin/..");
use POSIX qw/strftime/;
use Net::SNMP qw(:ALL);
use Time::HiRes qw( time sleep );
#FIXME - I had to add below for Perl 5.10 users.
# on Perl 5.10, I would get the following when running:
# perl -e"autoflush STDOUT, 1;"
# Can't locate object method "autoflush" via package "IO::Handle" at -e line 1.
use FileHandle;
# Create default logger, will reconfigure it as soon as we read configuration from database
my $log = Log::Fast->global();
my $myname = $0;
$myname =~ s{.*/}{}; # leave just program name without path
# Command line options
my $options = {
debug => 0,
verbose => 0,
logfile => "./$myname.log",
help => 0,
community => "public",
trapsource => "127.0.0.1",
timelimit => 1,
};
sub usage_and_exit {
my ($exit_code) = #_;
print STDERR qq{
This program is used to generate SNMP traps to a specified host at a specified rate
Usage: $myname [-o --option]
-h : this (help) message
-d : debug level (0-5) (0 = disabled [default])
-v : Also print results to STDERR
-l : log file (defaults to local dir
-r : Rate (events/sec)
-ts : host to generate messages FROM
-td : host to generate messages TO
-tl : Run for this many seconds (default 1)
-c : community
Example: $myname -td 192.168.28.29 -r 1 -tl 5 -v
};
exit($exit_code);
}
GetOptions(
'debug|d=i' => \$options->{debug},
'help|h!' => \$options->{help},
'verbose|v!' => \$options->{verbose},
'logfile|l=s' => \$options->{logfile},
'rate|r=i' => \$options->{rate},
'trapsource|ts=s' => \$options->{trapsource},
'trapdest|td=s' => \$options->{trapdest},
'community|c=s' => \$options->{community},
'timelimit|tl=i' => \$options->{timelimit},
) or usage_and_exit(1); # got some invalid options
if ( $options->{help} ) {
usage_and_exit(0);
}
# Reconfigure log to use logfile (as we finally got it from $settings), also
# set proper level and output based on $options{verbose} and $options{debug}
setup_log();
# Finally we are initialized, announce this to the world :-)
$log->INFO("Program initialized successfully");
my $date = strftime "%Y-%m-%d %H:%M:%S", localtime;
# start func
my $period = 1 / $options->{rate};
my $start = time();
my $limit = time() + $options->{timelimit};
my $total = $options->{rate} * $options->{timelimit};
$log->INFO("Generating $options->{rate} trap(s) every second for $options->{timelimit} seconds (1 every $period seconds, $total total events)");
while($start < $limit) {
my $elapsed = time() - $start;
if ($elapsed < $period) {
sleep($period - $elapsed);
my ($session, $error) = Net::SNMP->session(
-hostname => $options->{trapdest},
-community => $options->{community},
-port => SNMP_TRAP_PORT, # Need to use port 162
-version => 'snmpv2c'
);
if (!defined($session)) {
$log->INFO("ERROR: %s.", $error);
exit 1;
}
my $result = $session->snmpv2_trap(
-varbindlist => [
'1.3.6.1.2.1.1.3.0', TIMETICKS, 600,
'1.3.6.1.6.3.1.1.4.1.0', OBJECT_IDENTIFIER, '1.3.6.1.4.1.326',
'1.3.6.1.6.3.18.1.3.0', IPADDRESS, $options->{trapsource}
]
);
if (!defined($result)) {
$log->INFO("ERROR: %s.", $session->error());
} else {
$log->INFO("SNMPv2-Trap-PDU sent from $options->{trapsource} to $options->{trapdest}.");
}
} else {
$start = time();
}
}
#-------------------------------------------
# There should only be subs from here down
#-------------------------------------------
# =================================================================================================
# Helper functions
# =================================================================================================
# commify not used yet
sub commify {
my $text = reverse $_[0];
$text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;
return scalar reverse $text;
}
sub setup_log {
my $log_dir = dirname($options->{logfile});
# Create log dir, and build log path if not provided by command line option
if ( !-d $log_dir ) {
mkdir( $log_dir, 0755 ) or die("mkdir $log_dir: $!");
}
if ( !$options->{logfile} ) {
$options->{logfile} = $log_dir . "/" . basename( $0, '.pl' ) . '.log';
}
my $log_options = {};
# Set up output to file or both file and stderr
if ( $options->{verbose} ) {
# make multiplexer FH sending data both to file and STDERR
open( my $fh, '>>:tee', $options->{logfile}, \*STDERR )
or die("$options->{logfile}: $!");
$fh->autoflush(1);
$log_options->{fh} = $fh;
}
else {
open( my $fh, '>>', $options->{logfile} ) or die("$options->{logfile}: $!");
$log_options->{fh} = $fh;
}
# Setup extra information to put in every log line, depending on debug level
if ( $options->{debug} > 1 ) {
$log_options->{prefix} = "%D %T %S [%L] ";
}
else {
$log_options->{prefix} = "%D %T [%L] ";
}
$log_options->{level} = $options->{debug} > 0 ? 'DEBUG' : 'INFO';
$log->config($log_options);
$SIG{__WARN__} = sub {
my $msg = shift;
$msg =~ s/\n//;
$log->WARN($msg);
};
$log->INFO("Starting logging to $options->{logfile} with pid $$");
}
sub DEBUG {
my ( $level, #log_args ) = #_;
if ( $options->{debug} >= $level ) {
$log->DEBUG(#log_args);
}
}
Perhaps use something like Parallel::ForkManager ? In addition, with specific regard to testing scalability of your SNMP collector, you'll probably be interested in the use case of receiving the traps from many HOSTS, not just a single host sending traps at a high rate. For that, you might want to look at using pssh.
One problem might be the slowness of Net::SNMP in pure-perl - perhaps exectuting snmptest or snmptrap via the shell might be faster ? Worth a try.

mib name printing from mib values in perl

This is the code that I used to walk through the table in net:snmp using perl:
#! /usr/local/bin/perl
use strict;
use warnings;
use Net::SNMP qw(:snmp);
my $OID_hrSystem = '1.3.6.1.2.1.25.1';
my $OID_ifPhysAddress = '1.3.6.1.2.1.2.2.1.6';
my ($session, $error) = Net::SNMP->session(
-hostname => shift || 'localhost',
-community => shift || 'public',
-nonblocking => 1,
-translate => [-octetstring => 0],
-version => 'snmpv2c',
);
if (!defined $session) {
printf "ERROR: %s.\n", $error;
exit 1;
}
my %table; # Hash to store the results
my $result = $session->get_bulk_request(
-varbindlist => [ $OID_hrSystem ],
-callback => [ \&table_callback, \%table ],
-maxrepetitions => 10,
);
if (!defined $result) {
printf "ERROR: %s\n", $session->error();
$session->close();
exit 1;
}
# Now initiate the SNMP message exchange.
snmp_dispatcher();
$session->close();
# Print the results, specifically formatting ifPhysAddress.
for my $oid (oid_lex_sort(keys %table)) {
if (!oid_base_match($OID_ifPhysAddress, $oid)) {
printf "%s = %s\n", $oid, $table{$oid};
} else {
printf "%s = %s\n", $oid, unpack 'H*', $table{$oid};
}
}
exit 0;
sub table_callback
{
my ($session, $table) = #_;
my $list = $session->var_bind_list();
if (!defined $list) {
printf "ERROR: %s\n", $session->error();
return;
}
# Loop through each of the OIDs in the response and assign
# the key/value pairs to the reference that was passed with
# the callback. Make sure that we are still in the table
# before assigning the key/values.
my #names = $session->var_bind_names();
my $next = undef;
while (#names) {
$next = shift #names;
if (!oid_base_match($OID_hrSystem, $next)) {
return; # Table is done. chakri
}
$table->{$next} = $list->{$next};
}
# Table is not done, send another request, starting at the last
# OBJECT IDENTIFIER in the response. No need to include the
# calback argument, the same callback that was specified for the
# original request will be used.
my $result = $session->get_bulk_request(
-varbindlist => [ $next ],
-maxrepetitions => 10,
);
if (!defined $result) {
printf "ERROR: %s.\n", $session->error();
}
return;
}
Output is:
1.3.6.1.2.1.25.1.1.0 = 1 hour, 12:00.77
1.3.6.1.2.1.25.1.2.0 = �
+
1.3.6.1.2.1.25.1.3.0 = 1536
1.3.6.1.2.1.25.1.4.0 = BOOT_IMAGE=/boot/vmlinuz-3.0.0-14-generic root=UUID=5c4c8d22-3cea-4410-aaad-f297c75d217e ro quiet splash vt.handoff=7
1.3.6.1.2.1.25.1.5.0 = 1
1.3.6.1.2.1.25.1.6.0 = 133
1.3.6.1.2.1.25.1.7.0 = 0
But the required output for me is as follows:
hrSystemUptime.0 = 1:08:54.36
hrSystemDate.0 = 2011-12-14,16:0:2.0,+1:0
hrSystemInitialLoadDevice.0 = 1536
hrSystemInitialLoadParameters.0 = "BOOT_IMAGE=/boot/vmlinuz-3.0.0-14-generic root=UUID=5c4c8d22-3cea-4410-aaad-f297c75d217e ro quiet splash vt.handoff=7"
hrSystemNumUsers.0 = 1
hrSystemProcesses.0 = 133
hrSystemMaxProcesses.0 = 0
The main thing in the output is I want mib names to be printed in the output instead of the mib values
You could use the SNMP module (available on Ubuntu as libsnmp-perl) which offers a tied hash to loaded MIBs, %SNMP::MIB. Here's some example code:
use SNMP;
SNMP::initMib();
print "$SNMP::MIB{'1.3.6.1.2.1.25.1.1.0'}{label} = \n";
#Should print "hrSystemUptime = "
Because %SNMP::MIB is a tied hash, you can't just do a lookup and assign to a lexical variable, i.e. my $oid = $SNMP::MIB{$oidstr}. You have to access it directly every time.
There is lots of other information that it loads from the MIB, including data type, which could help with the issue it looks like you have with hrSystemDate. Also, see the man page for mib_api if you need to load specific MIBs. The ones you used in your example loaded by default on my system, though.
have you tried the snmpget command on your server? When I run snmpget direcly on CLI, the result cames with the name:
Ex: /usr/local/bin/snmpget -O Q -v 2c -c Community x.x.x.x .1.3.6.1.2.1.31.1.1.1.6.100663301
IF-MIB::ifHCInOctets.100663301 = 152528664859348
If it works for you, you might want to exectute the command in the PERL code, instead of using the LIB. Then you just have to handle with the output.
Also, tou can use snmptranslate to tranlate your OIDs:
Ex: /usr/local/bin/snmptranslate 1.3.6.1.2.1.25.1.1
HOST-RESOURCES-MIB::hrSystemUptime
More Info -> http://www.net-snmp.org/wiki/index.php/TUT:snmptranslate
EDIT
Why don't you:
my $pathSnmpTranslate = '/your/path/to/snmptranslate';
for my $oid (oid_lex_sort(keys %table)) {
my $oidTrans = `$pathSnmpTranslate $oid`;
if (!oid_base_match($OID_ifPhysAddress, $oid)) {
printf "%s = %s\n", $oidTrans, $table{$oid};
} else {
printf "%s = %s\n", $oidTrans, unpack 'H*',$table{$oid};
}
}
On my machine it worked:
> /xxx % /usr/local/bin/snmptranslate 1.3.6.1.2.1.25.1.1
HOST-RESOURCES-MIB::hrSystemUptime
> /xxx % /usr/local/bin/snmptranslate 1.3.6.1.2.1.25.1.1.0
HOST-RESOURCES-MIB::hrSystemUptime.0