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

I'm working on a script to ssh to a list of servers, execute a command, capture the output, then close the connection. The problem is that if there is a server on the list that is no longer available (ie gives 'connection closed by foreign host' immediately) the script bombs with the following error:
SSHProcessError The ssh process was terminated. at line xxx
This occurs on any reference to the $ssh object. Here are the commands that I'm running:
my $ssh = Net::SSH::Expect->new ( host => "$current_server", user => "$user", raw_pty => 1 ); # test the login eval { $login_output = $ssh->run_ssh(); }; # I've tried to capture $@ or $! here but it never gives me an +ything useful ($ssh->read_all(2) =~ />\s*|$\s*\z/) or die "where's the remot +e prompt?"; $ssh->exec("stty raw -echo");
It will die on the first command after the run_ssh, no matter what it is, and I can't seem to trap the fact that the connection was broken immediately. I've tried using peek to search for 'connection closed' or the like, but still unsuccessful.

I've heard one alternative is to use Net::SSH::Perl, however this is for my company and since we use ::Expect, they refuse to install the ::Perl module as well. Does anyone have any suggestions on how I should proceed?

Thanks!

Replies are listed 'Best First'.
Re: Problem with Net::SSH::Expect on unavailable hosts
by mr_mischief (Monsignor) on Jul 01, 2009 at 18:58 UTC
    Your code doesn't look like the code in the synopsis for the module. You're using the no password method, in which it shows that run_ssh() returns something false if it fails, but assigning that to $login_output which is the very same name of the variable in the first option of the synopsis, to log in with a username and password, where it gets the results of the login() method.

    The two methods are options, not mix and match. I'd suggest reading the Net::SSH::Expect docs a little closer. Either use one method or the other, but don't try to intermingle them. That's not the way they are documented to work.

      Other than being slightly misleading, there's nothing obviously wrong with his use of $login_output. He's capturing the return value of a function call, which is generally a good practice. If he were mixing $ssh->run_ssh() and $ssh->login(), then your critique would be valid.
        My critique is perfectly valid, thanks. His return value is what he needs to check, not $@ or $!. The reason he's getting nothing useful out of them is that nothing useful is being put into them. This is apparently because of the mingling of random parts of the documentation rather than reading them for comprehension.
Re: Problem with Net::SSH::Expect on unavailable hosts
by Khen1950fx (Canon) on Jul 01, 2009 at 23:13 UTC
    I used $ssh->login instead of $ssh->run_ssh(2).
    #!/usr/bin/perl use strict; use warnings; use diagnostics; use Net::SSH::Expect; my $ssh = Net::SSH::Expect->new ( host => "host", user => 'user', password => 'password', raw_pty => 1, timeout => 5 ); eval { my $login_output = $ssh->login(); if ($login_output !~ /Welcome/) { die "Login has failed. Login output was $login_output"; } }; $ssh->exec("stty raw -echo"); my $whoami = $ssh->exec('whoami'); print $whoami, "\n"; my $ls = $ssh->exec('ls -l /'); print $ls, "\n";
      Login is used for ID/PWD auth, run_ssh is for ID/key auth.
Re: Problem with Net::SSH::Expect on unavailable hosts
by jrsimmon (Hermit) on Jul 01, 2009 at 19:14 UTC
    Try setting a timeout, like so:
    my $ssh = Net::SSH::Expect->new ( host => "$current_server", user => "$user", raw_pty => 1, timeout => 5 ); # test the login eval { $login_output = $ssh->run_ssh(); }; # I've tried to capture $@ or $! here but it never gives me an +ything useful ($ssh->read_all(2) =~ />\s*|$\s*\z/) or die "where's the remot +e prompt?"; $ssh->exec("stty raw -echo");
    You may also want to look at the exp_debug and log_stdout options.
      How does that help? The return value that's being stuffed into $login_output is what needs to be tested to see if run_ssh() was successful or not. $@ and $! are meaningless when they are not the mechanism being used. How about suggesting testing the proper value?
        Without a timeout value, you can attempt to use the ssh connection before run_ssh() has finished what it's doing. This causes crazy stuff, like the script abending. In the op's example, testing the value of $login_output would not have necessarily solved anything. In the test script I wrote, it had no effect whatsoever (was always empty, whether the connection was successful or not). Additionally, adding the debug and log to stdout parms will help to track down what exactly is happening behind the run_ssh() call. If he waits until run_ssh() has finished, the regex check he included from the documentation will correctly determine whether he has a valid connection or not.

        The doc for Net::SSH::Expect does imply $! as the location meaningful error information from run_ssh() with the statement:
        $ssh->run_ssh() or die "SSH process couldn't start: $!";
        The fact that he "received nothing useful" from it leads me to believe he was checking trying to use the connection before run_ssh() had finished trying to connect.