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

SSH connections occasionally die randomly, and the computer I'm connecting to may also occasionally refuse connection. What I want is something that will allow me to continue whenever one of these errors occurs, rather than just dying and require a restart of the entire process.

To give you a small example, here is one function from my package, which is layered on top of Net::SFTP, which is in turn layered on top of Net::SSH::Perl:

sub openConnection { my $self = $_[0]; local $SIG{'__DIE__'} = sub {}; local $SIG{'__WARN__'} = sub {}; $self->{'sftp'} = Net::SFTP->new($::config{'ahost'}, user => $::config{'auser'}, password => $::config{'apass'}, ssh_args => [ options => [ 'ConnectTimeout 30', 'ServerAliveInterval 20', 'ServerAliveCountMax 3' ] ] ); if (!$self->{'sftp'}) { $self->{'error'} = "Unable to open SFTP connection"; return; } print "SFTP connection opened.\n"; return 1; }
This connects just fine when I use the correct credentials and the target server is running, but when I test it with a bad password or turn the target server off, I get a die from Net::SSH::Perl and that die does not get caught - even though I have $SIG{'__DIE__'} set. Wrapping the login inside eval { } causes scoping problems, and putting everything in a separate script and calling it via the command-line adds 30 seconds of overhead per transfer attempt, so those don't look like options either. What am I supposed to do?

I want to catch the die, detect that the connection attempt failed, and return false. Or turn off die somehow.

Replies are listed 'Best First'.
Re: Catching die in Net::SFTP / Net::SSH::Perl:
by salva (Canon) on Jul 15, 2010 at 06:17 UTC
    You should be able to do it using eval, what are your scoping problems?

    Well, anyway, you can also use Net::SFTP::Foreign. It will not die on that kind of errors:

    my $sftp = Net::SFTP::Foreign->new($::config{'ahost'}, user => $::config{'auser'}, password => $::config{'apass'}, more => [ 'ConnectTimeout 30', 'ServerAliveInterval 20', 'ServerAliveCountMax 3' ] ); if ($sftp->error) { $self->{error} = "Unable to open SFTP connection: " . $sftp->error +; return; } $self->{sftp} = $sftp;
Re: Catching die in Net::SFTP / Net::SSH::Perl:
by almut (Canon) on Jul 15, 2010 at 05:39 UTC
    ... What am I supposed to do?

    Fix the scoping problems with eval { }?  What exactly are they?

Re: Catching die in Net::SFTP / Net::SSH::Perl:
by TedPride (Priest) on Jul 15, 2010 at 07:33 UTC
    Hmm, I don't know if I was a character off on my syntax somewhere, but eval seems to work now without errors.

    On the other hand... I just broke Net::SFTP by trying to update some modules. So now I'm going to have to restore Perl from backup before I can test if this actually works or not. I was even using cpan to update, sometimes I really really hate Perl.


    EDIT: Let's say I'm on a cloud server (Unix variant, Apache, etc.) and connecting to it via SFTP and SSH to update and run my code. If I'm understanding correctly, Net::SFTP::Foreign doesn't require all those millions of broken dependencies like Net::SFTP does, but it does require an SSH client of some sort. What client would that probably be and how would I interface with it? Can you give me a simple connection example? If I don't explictly specify something, will it use the standard ssh and/or sftp utilities?

      Net::SFTP::Foreign (...) does require an SSH client of some sort. What client would that probably be and how would I interface with it?

      If the box running your script has Linux or some other Unix installed, Net::SFTP::Foreign will just use the SSH client in your system (as long as it is named ssh and is in the PATH). You can also install OpenSSH in a custom location and tell the module to use it with the ssh_cmd option. As it seems that you are using password authentication you will also have to install Expect.

      If you want to run the script under Windows, your best option may probably be to use the Net::SSH2 backend Net::SFTP::Foreign::Backend::Net_SSH2. The default backend does not support password authentication under Windows because of Expect not working there. Alternatively, you could use plink (read the FAQ section from the module documentation for the details).

      Examples of how to connect to a remote server are included in the docs. Usually, this will do:

      my $sftp = Net::SFTP::Foreign->new($host, user => $user, passwd => $pa +sswd); $sftp->error and die "SFTP connection failed: " . $sftp->error;
Re: Catching die in Net::SFTP / Net::SSH::Perl:
by msubbareddy (Initiate) on Oct 17, 2011 at 22:54 UTC
    If you still looking for hints on this module usage:
    #!/usr/bin/perl -w #usage: ./ -u xx use strict; use Net::SFTP; use Getopt::Long; my %opts; my $user; Getopt::Long::Configure('no_ignore_case'); GetOptions(\%opts, "v", 'u=s'=>\$user); my($host) = @ARGV; die "usage: demo1 [options] hostname" unless $host; # set up the arguments based on the command line options my %args = (ssh_args => []); $args{debug} = 1 if $opts{v}; push @{ $args{ssh_args} }, user => $user ; # make our connection print "Connecting to $host...\n"; my $sftp; #Stype0 # #$sftp = Net::SFTP->new($host); #$sftp->login($user, $pass); #OR #without PWD and using ssh key #$sftp->login($user); #Style1 # #$sftp = Net::SFTP->new($host, %args); #Style2 $sftp = Net::SFTP->new($host, ssh_args => [ user => $user, options => [ 'ConnectTimeout 30', 'ServerAliveInterval 20', 'ServerAliveCountMax 3' ] ] ); if (!$sftp) { print "Unable to open SFTP connection\n"; return; } print "SFTP connection opened.\n"; $sftp->ls("." , sub { print $_[0]->{longname}, "\n" }); $sftp->get('test.dat'); $sftp->status; print "Finished\n";