NAME

Net::SSH::Expect - SSH wrapper to execute remote commands

SYNOPSIS

	use Net::SSH::Expect;

	# configures the ssh connection and authentication
	my $ssh = Net::SSH::Expect->new (
	    host => "myserver.com", 
	    password=> 'pass87word', 
	    user => 'bnegrao', 
	    raw_pty => 1
	);

	# logon to the SSH server using those credentials.
	# test the login output to make sure we had success
	my $login_output = $ssh->login();
	if ($login_output !~ /Welcome/) {
	    die "Login has failed. Login output was $login_output";
	}

	# disable terminal translations and echo on the SSH server
	# executing on the server the stty command:
	$ssh->exec("stty raw -echo");

	# runs arbitrary commands and print their outputs 
	# (including the remote prompt comming at the end)
	my $ls = $ssh->exec("ls -l /");
	print($ls);
	
	my $who = $ssh->exec("who");
	print ($who);
	
	# When running a command that causes a huge output,
	# lets get the output line by line:
	$ssh->send("find /");   # using send() instead of exec()
  	while ( $ssh->hasLine() ) {
	    # returns the next line, removing it from the input stream:
	    print $ssh->readLine() . "\n";  
	}

	# take a look in what is immediately available on the input stream
	print $ssh->peek(0);	# you'll probably see the remote prompt
 
	# the last readLine() on the previous loop will not include the
	# remote prompt that appears at the end of the output, because the prompt
	# doesn't end with a '\n' character. So let's remove the remainder
	# prompt from the input stream:
	$ssh->eat($ssh->peek(0));  # removes whatever is on the input stream now

	# We can also iterate over the output in chunks,
	# printing everything that's available at each 1 second:
	$ssh->send ("find /home");
	my $chunk;
	while ($chunk = $ssh->peek(1)) { # grabs chunks of output each 1 second
	    $ssh->eat($chunk);
	}

	# Now let's run an interactive command, like passwd.
	# This is done combining send() and waitfor() methods together:
	$ssh->send("passwd");
	$ssh->waitfor('password:\s*\z', 1) or die "prompt 'password' not found after 1 second";
	$ssh->send("curren_password");
	$ssh->waitfor(':\s*\z', 1) or die "prompt 'New password:' not found";
	$ssh->send("new_password");
	$ssh->waitfor(':\s*\z', 1) or die "prompt 'Confirm new password:' not found";
	$ssh->send("new_password");

	# check that we have the system prompt again.
	my ($before_match, $match) = $ssh->waitfor('>\s*\z', 1);  # waitfor() in a list context
	die "passwd failed. passwd said '$before_match'." unless ($match);

	# closes the ssh connection
	$ssh->close();

DESCRIPTION

This module is a wrapper to the ssh executable that is available in your system's $PATH. Use this module to execute commands on the remote SSH server. It authenticates with the user and password you passed in the constructor's attributes user and password.

Once an ssh connection was started using the connect() method it will remain open until you call the close() method. This allows you execute as many commands as you want with the exec() method using only one connection. This is a better approach over other ssh wrapper implementations, i.e: Net::SCP, Net::SSH and Net::SCP::Expect, that start a new ssh connection each time a remote command is issued or a file is transfered.

It uses Expect.pm module to interact with the SSH server. A get_expect() method is provided so you can obtain the internal Expect object connected to the SSH server. Use this only if you have some special need that you can't do with the exec() method.

This module was inspired by Net::SCP::Expect http://search.cpan.org/~djberg/Net-SCP-Expect-0.12/Expect.pm and by Net::Telnet and some of its methods work the same as these two modules.

IMPORTANT NOTES ABOUT DEALING WITH SSH AND PSEUDO-TERMINALS

This module uses Expect to start the local ssh client process, and Expect will interact with this process through a local pseudo-terminal (ptty). Similarly, the ssh client will connect to the SSH server and there will receive an ssh login process attached to a ptty too.

During my tests I realized that the I/O to and from the ssh server changes drastically from OS to OS if we let the local and remote pttys configured on their defaults. The echo's and the \r\n translations make a mess that we are never sure what will be sent to the other side and what will be received here.

Many ptty features are system dependent and we can't rely on them working the same on different OS's.

To avoid these problems I always recommend you to:

1) enable the 'raw_pty' constructor attribute. This disables most (if not all) of the problematic features on the local ptty.

2) Similarly set the ptty on the remote server to 'raw -echo' as soon as you login. This can be done with:

$ssh->exec("stty raw -echo");

Obviously your server must support the 'stty' command for that.

3) If you won't run on the server interactive commands that prompt for input, like 'passwd', you could prevent the ssh server from attributing a ptty for the ssh login process. This is done by enabling the 'no_ptty' constructor attribute. What that does is passing the '-T' option to the ssh client process when it is created. From the BSD ssh client manual: -T Disable pseudo-tty allocation.

This will create the cleaner connection possible. You won't have a ptty on the server, and, weirdly, you won't receive a remote prompt. Try yourself 'ssh -T my.ssh.server' to see how it works. Notice that some system commands that rely on a terminal won't work, say, 'who am i', 'stty', etc.

Also, interactive commands like 'passwd' or 'mail' won't be able to print their prompts.

But other system commands will run better: 'ls -l' will be printed without terminal control characters. 'ps -ef' will have the command lines printed fully, since there is no 'columns' terminal limitation.

Moral of the story: pseudo terminals are bad, avoid them.

EXPORT

None by default.

CONSTRUCTOR ATTRIBUTES

The constructor accepts all the following attributes that can be set in the form of attribute => 'value' pairs. They are presentend in three groups: 1) attributes to configure the ssh client process; 2) attributes to configure the underlying Expect object; 3) attributes to configure this module;

ATTRIBUTES TO CONFIGURE THE SSH CLIENT PROCESS

Some of the attributes bellow will enable/disable some options of the ssh client. Refer to you ssh client documentation to know what each one does.

String user

the username to login.

String password

the password used to login.

String host

the address(dns name/ip) to the ssh server

String port

Feeds the -p ssh client option with alternate ssh port. This option is not set by default.

Boolean no_ptty

If enabled adds the -T ssh client option to the ssh command line. See the discussion on "IMPORTANT NOTES ABOUT DEALING WITH SSH AND PSEUDO-TERMINALS" to know if you want to enable this.

Char escape_char

Passes a character to the -e ssh client option. This enables ssh escapes. Since this option can cause trouble, it is explicitly turned off by default with a '-e none' option being set on the ssh command line.

String ssh_option

This lets you add your own ssh options to the command line. Set this string to the options you want, like '-v -p 2022', and your options will be added to the ssh command line that will start the ssh process.

CONSTRUCTOR OPTIONS THAT CONFIGURE THE INTERNAL EXPECT OBJECT

The following constructor attributes can be used to configure special features of the internal Expect object used to communicate with the ssh server. These options will be passed to the Expect object inside the connect method before it spawns the ssh process.

String log_file

Used as argument to the internal Expect->log_file() method. Default is no logfile.

Boolean log_stdout

Used as argument to the internal Expect->log_sdtout() method. Default is 0, to disable log to stdout.

Boolean exp_internal

Argument to be passed to the internal Expect->exp_internal() method. Default is 0, to disable the internal exposure.

Boolean exp_debug

Argument to be passed to the internal Expect->debug() method. Default is 0, to disable debug.

Boolean raw_pty

Argument to be passed to the internal Expect->raw_pty() method. It's recommended that you enable this. See the disscussion in "IMPORTANT NOTES ABOUT DEALING WITH SSH AND PSEUDO-TERMINALS" to know why. Default is 0 to let the local ptty as its defaults.

CONSTRUCTOR OPTIONS TO CONFIGURE THIS MODULE

terminator

the line terminator in use on the SSH server, this will added at the end of each command passed to the exec() method. The default is \n.

timeout

The maximum time in seconds to wait for a command to return to the PROMPT. The default is 10 seconds. Remember to increase this attribute with the timeout() method before you run a command that takes a long time to return.

debug

Causes some methods to print debug messages to the STDERR. This feature is not widely implemented yet. (only eat() implements it until this moment)

METHODS

login($test_success) # string login ([$test_success]) - authenticates on the ssh server. This should die if login fails. # param: # boolean $test_success: 0 | 1. if 1, login willdo an extra-text to verify if the password # entered was accepted. The test consists in verifying if, after sending the password, # the "Password" prompt shows up again. This indicates the password was rejected. # This test is disabled by default. # returns: # string: whatever the SSH server wrote in my input stream after loging in. This usually is some # welcome message and/or the remote prompt. You could use this string to do your verification # that the login was successful. The content returned is removed from the input stream. # dies: # IllegalState: if any of 'host' or 'user' or 'password' fields are unset. # SSHProccessError: if can't spawn the ssh process # SSHConnectionError: if the connection failed for some reason, # like invalid 'host' address or network problems.
exec($cmd [,$timeout]) - executes a command in the remote machine returning its output
waitfor($pattern [,$timeout]) # ($prematch, $match) = waitfor ($pattern [, $timeout]) # This method reads until a pattern match or string is found in the input stream. # All the characters before and including the match are removed from the input stream. # # In a list context the characters before the match and the matched characters are returned # in $prematch and $match. In a scalar context, the matched characters and all characters # before it are discarded and 1 is returned on success. On time-out, eof, or other failures, # for both list and scalar context, the error mode action is performed. See errmode(). # #
close() - terminates the ssh connection
send($string) - sends $string to the SSH server, returns nothing
peek([$timeout]) - returns what is in the input stream without removing anything
eat($string) - removes all the head of the input stream until $string inclusive. # eat() will only be able to remove the $string if it's currently present on the # input stream because eat() will wait 0 seconds before removing it. # # Use it associated with peek to eat everything that appears on the input stream: # # while ($chunk = $ssh->eat($ssh->peak())) { # print $chunk; # } # # Or use the readAll() method that does the above loop for you returning the accumulated # result. # # param: # string: a string currently available on the input stream. # If $string doesn't start in the head, all the content before $string will also # be removed. # # If $string is undef or empty string it will be returned immediately as it. # # returns: # string: the removed content or empty string if there is nothing in the input stream. # # debbuging features: # The following warnings are printed to STDERR if $ssh->debug() == 1: # eat() prints a warning is $string wasn't found in the head of the input stream. # eat() prints a warning is $string was empty or undefined. #
readAll([$timeout]) - reads and removes all the output from the input stream.

The reading/removing process will be interrupted after $timeout seconds of inactivity on the input stream.

get_expect() - returns a connected Expect object
hasLine([$timeout]) - tells if there is one more line on the input stream. Does not remove the line read.
readLine([$timeout]) - reads the next line from the input stream and returns it. The line read is removed from the input stream.
params:

none

returns:

an Expect object connected to the SSH server. It will die if you try to run it without being connected.

dies:

IllegalState: if this there is no valid ssh connection established

SEE ALSO

Net::SCP::Expect, Net::SCP, Net::SSH::Perl, Expect

REPORT YOUR TESTS TO ME, PLEASE

Please email me if you had problems or successes using my module. This way I can, one day, flag this module as "mature" in the modules database.

AUTHOR

Bruno Negrao Guimaraes Zica. <bnegrao@cpan.org>.

THANKS

Daniel Berger, author of Net::SCP::Expect. Special thanks to the people helping me improve this module by reporting their tests and the bugs they find.

COPYRIGHT AND LICENSE

Copyright (C) 2007 by Bruno Negrao Guimaraes Zica

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available.

5 POD Errors

The following errors were encountered while parsing the POD:

Around line 154:

You forgot a '=back' before '=head2'

Around line 159:

'=item' outside of any '=over'

Around line 225:

'=item' outside of any '=over'

Around line 241:

You forgot a '=back' before '=head1'

Around line 341:

You forgot a '=back' before '=head1'