unixgeek has asked for the wisdom of the Perl Monks concerning the following question:

Hello, I'm pretty much a complete newbie when it comes to Perl. I'm trying to create a small script to verify that telnet is active on a server, then log off and go to the next sequentially numbered server. Any errors I want dumped to a log file for review.

I think I have all of this working, EXCEPT that for some reason it fails to recognize that it is succeeding at logging in to the server, and then times out. The prompt char is a ">" which looks like it's included in the prompt portion ( Prompt => '/[\$%#>]\ $/');).

I've checked my error logs (dump.log), and I see that it logs in and gets to the prompt -- the script just doesn't recognize the prompt. It reads in the telnet login message and all.

Can anybody help me decipher the prompt portion or point out where I went wrong? I'm having trouble finding more examples/documentation on net::Telent online beyond the basic documentation file.

#!/usr/bin/perl use strict; use Net::Telnet (); #Trying to get default values working here -- still working on it my $clearfile; my $ipbase; # = shift || "192.168.1"; my $ipcurrent; # = shift || '1'; my $ipend; # = shift || '255'; my $port; # = shift || '23'; my $username; my $password; my $telnet; #This is the log file generated by any failed connections my $downreport = 'report.log'; print "Do you want to keep the old log file and append the new info? [ +y/n]\n"; $clearfile=<stdin>; chomp ($clearfile); if ($clearfile eq "n"){ open(INFO,">$downreport"); print INFO "\n";} #Grabbing all pertinent information -- the defaults are not there yet print "What is the base IP? (Example: 192.168.1)\n"; $ipbase = <stdin>; chomp ($ipbase); print "What is the bottom IP? (Default 1)\n"; $ipcurrent = <stdin>; chomp ($ipcurrent); print "What is the top IP? (Default 255)\n"; $ipend = <stdin>; chomp ($ipend); print "What port would you like to connect to (Default 23)?\n"; $port = <stdin>; chomp ($port); print "Username:"; $username = <stdin>; chomp ($username); #I would like to find a way to turn off echo or mask this portion print "Password:"; $password = <stdin>; chomp ($password); for($ipcurrent .. $ipend){ print "Connecting to $ipbase.$ipcurrent\n"; #This is the TRY part of the try-catch portion, where it tries to conn +ect #If it doesn't it outputs the error without killing the program eval { #Sets parameters for the telnet session $telnet = new Net::Telnet->new( Timeout=>3,Dump_Log=>'dump.log', input +_log=>'input.log', output_log=>'output.log', Port=>$port, Prompt => '/[\$%#>]\ $/'); die &Log_Message("Can't open telnet session to the remote host") unles +s #warn &Log_Message("Can't open telnet session to remote host") unless $telnet; #opens the particular IP that we're at. Not sure if these "die" comma +nds are necessary $telnet->open("$ipbase.$ipcurrent"); #die ("Cannot open $ipbase.$ipcurrent\n"); #This may need some tweaking depending on the log-on format $telnet->login( Name=>"$username", Password=>"$password"); print "Logged on\n"; print $telnet->getline; #once connected it tries to exit cleanly $telnet->print("exit"); }; #The other part was try, this is the CATCH. It catches the errors and +logs them if ($@){ print $@; print "$ipbase.$ipcurrent failed.\n"; open(INFO, ">>$downreport"); print INFO "Failure at \t $ipbase.$ipcurrent \t Please investigate +.\n"; }; print "Trying the next IP\n"; #Stepping up to the next IP $ipcurrent++; } exit 0;

Replies are listed 'Best First'.
Re: Login failure with net::Telnet
by McDarren (Abbot) on Dec 23, 2006 at 01:15 UTC
    Your immediate problem has been solved, but here are a few other pointers that may help.
    1. #Trying to get default values working here -- still working on it

      Check out Getopt::Long - makes processing command line arguments a snap.

    2. You can save yourself a bit of typing, by doing:
      chomp($ipbase = <STDIN>);
      Instead of:
      $ipbase = <stdin>; chomp ($ipbase);

    3. #I would like to find a way to turn off echo or mask this portion print "Password:"; $password = <stdin>;
      There are several ways to do this - check out perlfaq8. (You'll probably want to use Term::ReadKey)

    4. It's always a good idea to check the return status of any filehandle operations (and a bad idea not to), because you never know what may have gone wrong outside your program. So..
      open(INFO,">$downreport");
      Is better written as:
      open INFO, '>', $downreport or die "Cannot open $downreport:$!\n";
      Note that any OS error will be returned as the contents of $!. Also note the use of open with three arguments, instead of two. This is generally accepted as a better (and safer) practice.
      Actually, even that is a bit outdated. These days perl allows you to use normal scalar variables as filehandle references. So you can do something like:
      open my $info, '>', $downreport or die "Cannot open $downreport:$!\n";
      See the docs on open for more information.

    5. One last point... anytime you find yourself repeating code, then thats a pretty good sign that you need to do a bit of abstraction, and break some of your code out into a subroutine. In your case, it's all the gathering of user input that you do via STDIN (Note that is is common practice to write STDIN, STDOUT and STDERR in all upper-case). So instead of:
      print "What is the base IP? (Example: 192.168.1)\n"; $ipbase = <stdin>; chomp ($ipbase); print "What is the bottom IP? (Default 1)\n"; $ipcurrent = <stdin>; chomp ($ipcurrent); print "What is the top IP? (Default 255)\n"; $ipend = <stdin>; chomp ($ipend); # etc....
      You could do something like:
      sub get_user_input { my $prompt = shift; print "$prompt\n"; return chomp(<STDIN>) }
      And then:
      $ipbase = get_user_input("What is the base IP? (Example: 192.168.1)"); $ipcurrent = get_user_input("What is the bottom IP? (Default 1)"); $ipend = get_user_input("What is the top IP? (Default 255)");
      There are further levels of abstraction that could be applied if you wanted to (a dispatch table, for example) - but I'm sure you get the idea ;)

    Hope all the above helps,
    Cheers,
    Darren :)

Re: Login failure with net::Telnet
by andyford (Curate) on Dec 22, 2006 at 19:00 UTC

    The regular expression that you are using to match the prompt

    /[\$%#>]\ $/
    is looking for a dollar, hash or pound followed by a space followed by dollar.

    To match just a ">", change it to

    />/

    non-Perl: Andy Ford

Re: Login failure with net::Telnet
by jettero (Monsignor) on Dec 22, 2006 at 18:53 UTC

    I think you just need to remove that $telnet->getline and that $telnet->print("exit") and try something like this instead:

    my @response = $telnet->cmd("exit"); print $_ while @response;

    The print() and waitfor() functions work differently than the automatic prompt detector (Prompt=>regular) and the cmd() function.

    -Paul

Re: Login failure with net::Telnet
by unixgeek (Initiate) on Dec 22, 2006 at 20:24 UTC
    Thanks a ton! I have it up and working now. Going to implement more features such as a timer between connect attempts as to avoid looking like a portscan/other nastiness.

    Also, I'm going to try giving the user a little more immediate feedback.

    Thanks again!