Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

A little demo for Net::SSH2

by zentara (Archbishop)
on Aug 25, 2006 at 18:18 UTC ( #569657=snippet: print w/ replies, xml ) Need Help??

Description: UPDATE AUG 28 2007: Revisited this, and added code to prevent blocking on channel or shell use. Also note that a single channel can only exec 1 command, so use shell for multiple commands. Also attempts to create multiple channels or sftp objects will fail. Tested with the latest libs, openssl-0.9.8e , openssh-4.6p1, libssh2-0.17, Net-SSH2-0.10

From the Net::SSH2 README

Net::SSH2 is a perl interface to the libssh2 (http://www.libssh2.org) library. It supports the SSH2 protocol (there is no support for SSH1) with all of the key exchanges, ciphers, and compression of libssh2. At present, libssh2 requires OpenSSL (http://www.openssl.org) and can optionally use zlib for compression (http://www.zlib.net). The Net::SSH::Perl modules are showing signs of age; with the requirement to be compatible with SSH versions 1 and 2, support of newer features such as channels is difficult; it also has many dependencies, some of which are hard to build (e.g. Math::Pari).

So to help you get into it, here is a simple demo of what works, including a key authorization example. Some of the docs are hard to deal with because of way xs works, so here are some working usages that should allow you to handle almost anything, sftp, scp, running scripts, etc.

#!/usr/bin/perl
use warnings;
use strict;
use Net::SSH2;
use Data::Dumper;

# see maillist archives at
# http://lists.sourceforge.net/lists/listinfo/ssh-sftp-perl-users

my $ssh2 = Net::SSH2->new();
$ssh2->connect('localhost') or die "Unable to connect Host $@ \n";
#this works for passwords 
#$ssh2->auth_password('z','ztester') or die "Unable to login $@ \n";

#do key authorization like commandline shell
# ssh -o PreferredAuthentications=publickey localhost
# See: http://cfm.gs.washington.edu/security/ssh/client-pkauth/
# for setting up the keys

# this dosn't work
#$ssh2->auth(username=>'z', interact => 1);

# so get the password for the key 
use Term::ReadKey;
print "And your key password: ";
ReadMode('noecho');
chomp(my $pass = ReadLine(0));
ReadMode('restore');
print "\n";

# works when run from z's homedir because you need
# permission to read the keys
$ssh2->auth_publickey('z',
                 '/home/z/.ssh/id_dsa.pub',
         '/home/z/.ssh/id_dsa',
          $pass );

#works
my $sftp = $ssh2->sftp();
my $fh = $sftp->open('/etc/passwd') or die;
print $_ while <$fh>;

#my $chan = $ssh2->channel();
#$chan->blocking(0);
#$chan->exec('ls -la');
#while (<$chan>){ print } 

# to run a remote command in the background
# you need to semi-daemonize it by redirecting it's filehandles
my $chan3 = $ssh2->channel();
$chan3->blocking(1);
$chan3->exec("nohup /home/zentara/perlplay/net/zzsleep > foo.out 2> fo
+o.err < /dev/null &");
$chan3->send_eof;


#to run multiple commands use a shell
#shell use
my $chan = $ssh2->channel();
$chan->blocking(0);
$chan->shell();
print $chan "ls -la\n";
print "LINE : $_" while <$chan>;
print $chan "who\n";
print "LINE : $_" while <$chan>;
print $chan "date\n";
print "LINE : $_" while <$chan>;



# file and directory operations
#will get dir named 2
#my $chan1 = $ssh2->channel();
#$chan1->exec('ls -la 2');
#while (<$chan1>){ print } 

# mkdir with sftp
#my $sftp = $ssh2->sftp();
my $dir = '/home/z/3';
$sftp->mkdir($dir);
my %stat = $sftp->stat($dir);
print Dumper([\%stat]), "\n";

#put a file
my $remote = "$dir/".time;
$ssh2->scp_put($0, $remote);

#get a file
#use IO::Scalar;
#my $check = IO::Scalar->new;
#$ssh2->scp_get($remote, $check);
#print "$check\n\n";

# get a dirlist
my $dir1 = '/home/z';
my $dh = $sftp->opendir($dir1);
while(my $item = $dh->read) {
    print $item->{'name'},"\n";
    }

#shell use
my $chan2 = $ssh2->channel();
$chan2->blocking(0);
$chan2->shell();
print $chan2 "uname -a\n";
print "LINE : $_" while <$chan2>;
print $chan2 "who\n";
print "LINE : $_" while <$chan2>;
$chan2->close;

__END__

Comment on A little demo for Net::SSH2
Download Code
Re: A little demo for Net::SSH2
by Anonymous Monk on May 22, 2007 at 12:24 UTC
    Hi, have you tested in windows?. scp_put,shell is not working in windows.
      I don't have MSWindows on any of my machines.

      I'm not really a human, but I play one on earth. Cogito ergo sum a bum
      To use Shell method on Active Perl on windows use:
      #!/usr/bin/perl use warnings; use strict; use NET::SSH2; my $host = ""; # use the ip host to connect my $user = ""; # your account my $pass = ""; # your password my ($len, $buf); my $ssh2 = Net::SSH2->new(); #$ssh2->debug(1); if ($ssh2->connect($host)) { if ($ssh2->auth_password($user,$pass)) { #shell use my $chan = $ssh2->channel(); $chan->shell(); $chan->write("ls -a\n"); select(undef,undef,undef,0.2); print $buf while ($len = $chan->read($buf,512)) > 0; $chan->write("who\n"); select(undef,undef,undef,0.2); print $buf while ($len = $chan->read($buf,512)) > 0; #$chan->send_eof(); $chan->close; } else { warn "auth failed.\n"; } } else { warn "Unable to connect Host $@ \n"; }

        Thanks for the start, but I found your code would hang on my system. But, all I had to do was make the channel non-blocking and it worked.

        if( $ssh2->connect($host) ) { if( $ssh2->auth_password($user,$pass) ) { #shell use my $chan = $ssh2->channel(); $chan->blocking(0); $chan->shell(); $chan->write("ls -a\n"); select(undef,undef,undef,0.2); print $buf while defined ($len = $chan->read($buf,512)); $chan->write("who\n"); select(undef,undef,undef,0.2); print $buf while defined ($len = $chan->read($buf,512)); $chan->close; } else { warn "auth failed.\n"; } }


        TGI says moo

      To use Shell method on Active Perl on windows use:

      #!/usr/bin/perl
      use warnings;
      use strict;
      use NET::SSH2;

      my $host = ""; # use the ip host to connect
      my $user = ""; # your account
      my $pass = ""; # your password

      my ($len, $buf);

      my $ssh2 = Net::SSH2->new();
      #$ssh2->debug(1);
      if ($ssh2->connect($host)) {
      if ($ssh2->auth_password($user,$pass)) {
      #shell use
      my $chan = $ssh2->channel();
      $chan->shell();

      $chan->write("ls -a\n");
      select(undef,undef,undef,0.2);
      print $buf while ($len = $chan->read($buf,512)) > 0;

      $chan->write("who\n");
      select(undef,undef,undef,0.2);
      print $buf while ($len = $chan->read($buf,512)) > 0;
      #$chan->send_eof();
      $chan->close;
      } else {
      warn "auth failed.\n";
      }
      } else {
      warn "Unable to connect Host $@ \n";
      }
        is it possible to poll the shell for timeouts just like this example:
        $chan->exec("ls -l"); my $poll = { handle => $chan, events => -1 }; while ($ssh2->poll(500, [ $poll ])) { if ($poll->{revents}{out}) { while (<$chan>) { $r=$r.$_; } } if ($poll->{revents}{channel_closed} || $poll->{revent +s}{listener_closed}) { my $exit = $chan->exit_status(); print $r; } } print "timeout\n";
        This works fine for $chan->exec(..), but i need shell to execute more than one command, can someone help me with some code to poll the shell writes and reads for timeouts???
Re: A little demo for Net::SSH2
by Anonymous Monk on Dec 04, 2007 at 23:48 UTC
    Seems that scp_put doesn't work from linux to HP-UX. Can you confirm ?

      FYI, I just tried from Linux to HP-UX 11.00 (PA-RISC) and to HP-UX 11.23 (Itanium2), and both worked nicely.


      Enjoy, Have FUN! H.Merijn
Re: A little demo for Net::SSH2
by runrig (Abbot) on Aug 10, 2010 at 15:10 UTC
    I found that some of the commands I was running over ssh (from Windows to an HP-UX system) were returning early from the read loop (and terminating the command), so I had to add extra code for executing and reading the results. This was part of a larger module, but the relevant method was:
    sub cmd { my $self = shift; my $cmd = shift; my $ssh = $self->{ssh}; my $ch = $self->{ch}; unless ($ch) { $self->{ch} = $ch = $ssh->channel; $ch->ext_data('merge'); $ch->blocking(0); $ch->shell(); 1 while <$ch>; } print $ch "$cmd\n"; select(undef, undef, undef, 0.2); print $ch "print SSH2_cmd_done\n"; select(undef, undef, undef, 0.2); my @output; READ_LOOP: while (1) { local $_; while (<$ch>) { last READ_LOOP if /^SSH2_cmd_done/; push @output, $_; } sleep 1; } return @output; }

      I am using what I can glean from this thread to try to ssh into a cisco box and get a portion of it's config. What I am using below executes the two commands, sleeps 45 seconds, and by the time it gets past the sleep loop it has already closed the shell. Is there any way to make the shell stay open until I close it, if that makes sense? Like a keepalive of some kind?

      if ($ssh) { eval { my $cs = Net::SSH2->new(); $cs->connect($_); my $ip = $_; my $done = false; open (OUTFILE, ">$ip.txt"); if ($cs->auth_keyboard($user,$pw)) # login { print "Opening SSH session to $_\n"; my $chan2 = $cs->channel(); $chan2->shell(); print $chan2 "term len 0\n"; print $chan2 "sh run | be line\n"; # until (<$chan2> =~ /line con 0/i) { # print "in the line con 0 loop"; print <$chan2>; # }; # prints everything above the "line con 0" line # print OUTFILE "line con 0\n"; # pri +nts the "line con 0" line # until ($done) { # print "do we even enter the until loop?\n\n"; # if (<$chan2> =~ /new/i) { # print "we are done!!!\n\n\n"; # $done = true; # } # print "the similarity of chan2 and end is " . (<$chan2> = +~ /new/i) . "\n"; # print <$chan2> . "here is another line " . $done; + # } # prints everything below the "line con 0" line my $count = 45; while ($count > 0) { print --$count; sleep 1; } until (<$chan2> =~ /line con 0/i) {1; }; print $chan2 "exit\n"; print "chan2 is empty, and we are awake\n"; close (OUTFILE); $chan2->close; # Log out of device print LOG "SSH to " . $ip . "\n"; } }; # End trying ssh

      so, I fixed it, with

      if ($ssh) { eval { my $cs = Net::SSH2->new(); $cs->connect($_); my $ip = $_; open (OUTFILE, ">$ip.txt"); if ($cs->auth_keyboard($user,$pw)) # login { print "Opening SSH session to $_\n"; my $chan2 = $cs->channel(); $chan2->shell(); print $chan2 "term len 0\n"; print $chan2 "sh run | be line\n"; print OUTFILE <$chan2>; print OUTFILE "line con 0\n"; + # prints the "line con 0" line print OUTFILE <$chan2>; + # prints everything below the "line con 0" line until (<$chan2> =~ /line con 0/i) {1;}; print OUTFILE <$chan2>; + # prints everything below the "line con 0" line close (OUTFILE); $chan2->close; # Log out of device print LOG "SSH to " . $ip . "\n"; } }; # End trying ssh

      It must have been the blocking missing, and the untils were messing it up.

        It must have been the blocking missing,

        I didn't see you have a $chan->blocking(0/1), or if that is the type of blocking you are talking about.

        If you google for "Net::SSH2 timeout" you will find it has been tricky to get timeouts to work. The latest module has a few lines where timeouts can be set

        connect ( handle | host [, port [, Timeout => secs ]] )
        and
        poll ( timeout, arrayref of hashes ) #probably the timeout you want
        You might want to look at Net::SSH2 Command Timeout Before Completion for other modules that handle the timeouts better.

        I'm not really a human, but I play one on earth.
        Old Perl Programmer Haiku ................... flash japh
Re: A little demo for Net::SSH2
by ryber (Acolyte) on Jan 13, 2011 at 23:49 UTC

    Thanks for the example, Zentara, it has been helpful in trying to get Net::SSH2 up and running. Do you (or anyone else on this thread) have any experience using Net::Telnet or Net::Telnet::Cisco on top of Net::SSH2, for example, by using Net::Telnet's fhopen() method to direct it to use the Net::SSH2 connection?

    Or to hack Net::Appliance::Session to use Net::SSH2 for its SSH transport, instead of the current logic, which forks a new process and runs an external ssh binary?

Re: A little demo for Net::SSH2
by perl514 (Pilgrim) on Jan 16, 2013 at 14:42 UTC

    Hi Zentara,

    Thanks for this tutorial. It helped me create a script for automating checks on our NAS Arrays to check for failures.

    Perlpetually Indebted To PerlMonks

    Win 7 at Work. Living through it....Linux at home. Loving it.

      Hello All, I used an example from this tutorial in my code, which executes a command which has -i -c parameters(interval, no. of iterations). However Once the command completes the control never returns to the PERL code. Can anyone please help??? Below is the code:
      use Net::SSH2; my $ssh2 = Net::SSH2->new(); $ssh2->connect('localhost') or die $!; $ssh2->auth_password('nasadmin','adminpassword') or die "Unable to log +in $@ \n"; my $chan = $ssh2->channel(); $chan->shell(); $chan->write("server_stats server_2 -monitor cifs-std -i 5 -c 5 \n"); select(undef,undef,undef,0.2); print $buf while ($len = $chan->read($buf,32)) > 0; $chan->close;
        I suspect your server_stats command is taking too long to generate output. Does it work with a "nas_server -list" command?

Back to Snippets Section

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: snippet [id://569657]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (5)
As of 2014-09-17 03:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (56 votes), past polls