|
Softpanorama
(slightly skeptical)
Open Source Software Educational Society |
May the
source be with you,
but remember the KISS principle ;-)
|
Using Perl to simulate Telnet from a Program
If you are running a monitoring application it is often
useful to
simulate a telnet connection from your program to a remote
machine using telnet, then issues one or a couple of commands and
process output to generate alerts or status report.
the simplest
way to accomplish this is to use Expect (or
Perl Expect.pm) but you can use the CPAN module
Net::Telnet too:
use Net::Telnet;
$t = Net::Telnet->new( Timeout => 10,
Prompt => '/%/',
Host => $hostname );
$t->login($username, $password);
@files = $t->cmd("ls");
$t->print("top");
(undef, $process_string) = $t->waitfor('/\d+ processes/');
$t->close;
Net::Telnet provides an interface to the
telnet protocol. If has fake and generally unnecessary object-oriented
flavor: you first need to create a connection with Net::Telnet->new, and then
interact with the remote machine using method calls on the resulting
object.
Give the new method named parameters, passed in
hash-like form. We'll only cover only a few of many possible parameters.
The most important is Host, the machine you're telnetting to. The
default host is localhost. If you want to telnet to a port other than
one telnet normally uses, specify this in the Port option. Error
handling is done through the function whose reference is specified in
the Errmode parameter.
Another important option is Prompt. When you log in or
run a command, Net::Telnet uses the Prompt pattern to determine when the
login or command has completed. The default Prompt is:
/[\$%#>] $/
which matches the common shell prompts. If the prompt on
the remote machine doesn't match the default pattern, you have to
specify your own. Remember to include the slashes.
Timeout lets you control how long (in seconds) network
operations wait before they give up. The default is 10 seconds.
If an error or timeout occurs in the Net::Telnet module,
the default behavior is to raise an exception, which, if uncaught,
prints a message to STDERR and exits. To change this, pass a subroutine
reference to new in the Errmode argument. If instead of a code
subroutine, you specify the string "return" as the Errmode, methods
return undef (in scalar context) or an empty list (in list context) on
error, with the error message available via the errmsg method:
$telnet = Net::Telnet->new( Errmode => sub { main::log(@_) }, ... );
The login method is used to send a username and password
to the remote machine. It uses the Prompt to decide when the login is
complete and times out if the machine doesn't reply with a prompt:
$telnet->login($username, $password)
or die "Login failed: @{[ $telnet->errmsg() ]}\n";
To run a program and gather its output, use the cmd
method. Pass it the string to send, and it returns the output of the
command. In list context, it returns one line per list element. In
scalar context, it returns one long line. It waits for the Prompt before
returning.
You can separate the sending of the command from the
reception of its output with the print and waitfor methods, as we do in
the Solution. The waitfor method takes either a single string containing
a Perl regular expression match operator:
$telnet->waitfor('/--more--/')
or named arguments. Timeout lets you specify a timeout
to override the default, Match is a string containing a match operator
as above, and String is a literal string to find:
$telnet->waitfor(String => 'greasy smoke', Timeout => 30)
In scalar context, waitfor returns true if the pattern
or string was found. If it is not found, the Errmode action is
performed. In list context, it returns two strings: all the text before
the match, and the text that matched.
See also the documentation for the Net::Telnet module
from CPAN; RFCs 854-856, as amended by later RFCs
Hi,
I'm trying to login to a router using telnet and EXPECT, to
backup the
router config.
When I'm connecting to the device via telnet, everything is
ok, but when I'm
using a perl/expect-Script to connect, the router asks
"Press any key to
continue", even before the login prompt.
*** via telnet commandline:
# telnet 1.2.3.4
Trying 1.2.3.4...
Connected to 1.2.3.4.
Escape character is '^]'.
One200
Username:
*** via script
# ./save-cpe.pl
Trying 1.2.3.4...
Connected to 1.2.3.4.
Escape character is '^]'.
One200
Press any key to continue (Q to quit)
This is the perl code I spawn telnet with:
$telnet = Expect->spawn('telnet', $ipaddr)
or error_exit(11, "....");
Looks like the router thinks the screen height is just a few
lines?
Any ideas?
Thanks,
Re: "expect" spawns telnet -- screen height
> This is the perl code I spawn telnet with:
>
> $telnet = Expect->spawn('telnet', $ipaddr)
> or error_exit(11, "....");
>
> Looks like the router thinks the screen height is just
a few lines?
OK, I've found out that the following code works ok when
run from a bash
shell:
my $telnet = new Expect;
$telnet->raw_pty(1);
if (defined ($telnet->slave)) {
$telnet->slave->clone_winsize_from(\*STDIN);
}
$telnet->spawn('telnet', $ipaddr)
or error_exit(11, ".....");
Unfortunately, I'm running this script from a PHP Page,
NOT from a bash
shell... perl teminates without any hint when reaching
the line
"$telnet->slave->clone_winsize_from(\*STDIN);".
Any Idea how to set the winsize to e.g. 80x25 without
cloning it or where to
clone it from when run from a PHP script?
Re: "expect" spawns telnet -- screen height
My solution, based on Jens script, is not very
elegant, but it works
from crontab (for example) :
open TTY,"/dev/console" or die "not connected to
a terminal\n";
$telnet->slave->clone_winsize_from(\*TTY);
close TTY;
Daniel
--
dpratlong
>Suppose you needed to open
a connection to a remote host from within your perl program.
One thing you would probably think of doing at first is the
following:
open TELNET "|telnet $hostname"; print TELNET
"$username\n"; print TELNET "$password\n"; ...
Unfortunately, if you try this, you'll find it doesn't work.
The telnet program connects to the remote host but it
completely ignores any commands you pipe to it. That's
because the telnet program reads its input only from the
terminal and not from standard input.
The most cunning among you might then think a workaround for
this: Open a socket to port 23 (the default telnet port) of
the remote machine and write directly to it, thus, bypassing
the telnet program altogether. Well, although that might
sound like a neat idea, it's not. And that's because the
telnet protocol requires that certain control data be
exchanged between the two machines by sending them along
through the same socket connection.
So, it turns out that the problem is much more complicated
than it seemed at first. We don't just need to write code to
perform socket I/O, but also we need to write code that
speaks the TELNET protocol. This is the bad news. The good
news is that this code has already been written, and that
its author was kind enough to bundle it in a useful module,
Net::Telnet, available at
CPAN.
Net::Telnet
Using Net::Telnet is pretty straightforward and simple.
Let's first see a no-thrills example:
use Net::Telnet; $telnet = new Net::Telnet (
Timeout=>10, Errmode=>'die');
$telnet->open('camel.perlfect.com');
$telnet->waitfor('/login: $/i'); $telnet->print('bilbo');
$telnet->waitfor('/password: $/i');
$telnet->print('baggins'); $telnet->waitfor('/\$ $/i');
$telnet->print('who'); $output = $telnet->waitfor('/\$
$/i'); print $output;
This simple program connects to camel.perlfect.com with
username and password, 'bilbo' and 'baggins' respectively,
and the issues the command 'who' to get a list of logged in
users. It then retrieves and prints the output. Although the
code is self explanatory, here are a few things worth of
noting:
- The Errmode option in the constructor for
the telnet object specifies what kind of behaviour we
want the object to have when it encounters an error. die
means that the program will die with an error message.
The other option is return. This will cause the method
that caused the error to return a false value. The error
message can then be retrieved from
Net::Telnet->errmsg.
- The method waitfor() takes a regular
expression as an argument and tries to match it in the
stream that is transmitted from the remote host. Upon a
successful match, the method returns all input before
the match.
Shortcuts
You might notice that issuing commands involves repeating
print() and waitfor() calls in a very much
similar manner. We print a command and then we try to match
a shell prompt. Net::Telnet provides a very nice
mode of operation that rids you of some of the repetitive
tt. All you need to do is to specify the regular expression
that matches a prompt as a parameter in the object's
constructor and then use the cmd() method to issue commands.
Similarly to command issuing, Net::Telnet provides
a handy method to simplify the login process, namely
login(). The following example demonstrates these
shortcuts.
use Net::Telnet; $telnet = new Net::Telnet (
Timeout=>10, Errmode=>'die' Prompt => '/\$ $/i');
$telnet->open('camel.perlfect.com'); $telnet->login('bilbo',
'baggins'); print $telnet->cmd('who');
This does the same as the previous example, but with much
less typing. Note that the login() method matches
the login and password prompts with the regular expressions
/(login|username)[: ]*$/i and /password[: ]*$/i
respectively.
So far we have covered the basics of the Net::Telnet module,
enough to get you going with your first telnetting scripts.
The module is rich with other features, so make sure you
take the time to have a look at the documentation.
Happy TELNETing!
- The Net::Telnet documentation that comes with its
distribution (Net::Telnet comes as part of the libnet
bundle at CPAN) covers
Simple Telnet
Automation Using Expect
Using perl to
connect to remote hosts via telnet.
Perl Cookbook Solutions and ... - Google Book Search
O'Reilly -
Safari Books Online - 1565922433 - Perl Cookbook
Copyright © 1996-2008 by Dr. Nikolai Bezroukov.
www.softpanorama.org was
created as a service to the UN Sustainable Development Networking Programme (SDNP)
in the author free time.
Submit
comments This document is an industrial compilation designed and created
exclusively for educational use and is placed under the copyright of the
Open Content License(OPL).
Original materials copyright belong to respective owners. Quotes are made
for educational purposes only in compliance with the fair use doctrine.
Standard disclaimer: The statements, views and opinions presented on
this web page are those of the author and are not endorsed by, nor do they necessarily
reflect, the opinions of the author present and former employers, SDNP or any other
organization the author may be associated with. We do not warrant the correctness
of the information provided or its fitness for any purpose.
Last modified:
November 28, 2008