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

Dear monks,

Are there any Net::SSH2 experts in the house?

I'm working with an application at work that pulls code and config out of version control, packages it, pushes it up to a target server, and then deploys the package.

Currently, the communication to the remote servers is done with calls to the ssh binary on the local server. I was hoping to replace these invocations of /usr/bin/ssh with a perl module.

I had a look at Net::SSH:Perl, but had a bit of grief installing that due to the dependencies on various math modules. I'm now working with Net::SSH2, which is certainly a lot easier to install, and I'm not sure whether the problems I'm having are expected behaviour, or a bug.

I would like to be able to issue a single command to a server, reading any output to stdout and stderr, and the exit code of the remote process. I'm trying to use the exec method of Net::SSH2::Channel, but at the moment, I'm having trouble reading the output of my command. If I use the channel object as a file handle and read from that, I typically only get a couple of characters back in output before the SSH channel closes. I've tried using the poll and read methods with slightly more success, but I still only get around a couple of lines from say, 'ls -la' in a directory where I would expect to see ~20 lines of output.

I could use the shell method, but then it seems like there's no easy way to separate stderr for stdout, or to retrieve the exit code.

I wonder if this is expected behaviour for exec. To run a remote command, then immediately exit without waiting for output. Can anyone confirm that?

Here's some sample code and output. Am I doing anything obviously wrong?

#!/usr/bin/perl -w use strict; use Data::Dumper; use Net::SSH2; my $remote_host = 'remote'; my $remote_user = 'vortura'; my $remote_pass = 'xxxxxxx'; my $ssh2 = Net::SSH2->new(); $| = 1; $ssh2->connect($remote_host) or die "connect failed"; $ssh2->auth_password( $remote_user, $remote_pass ) or die "auth failed +"; my $chan = $ssh2->channel(); $chan->blocking(0); my $ret = $chan->exec("ls -la /Users/vortura"); while(<$chan>) { print "OUT: ", $_; } print "\n"; print $ret, "\n"; $chan->close;

Output:

vortura $ ./sshtest.pl OUT: total 0
'total' in the output being the first few lines of output from ls on the remote system.

Many thanks, R

Replies are listed 'Best First'.
Re: Printing Output From Net::SSH2 exec
by syphilis (Archbishop) on May 14, 2009 at 09:34 UTC
    I've got a code segment like this (for use on Win32):
    . . my $ssh2 = Net::SSH2->new(); $ssh2->connect('xxx.xxx.xxx.xxx') or die; $ssh2->auth_password('user','pass') or die "Unable to login $! \n"; my $chan1 = $ssh2->channel(); $chan1->blocking(1); $chan1->exec('echo $PATH'); $chan1->read($buf, $buflen); print "BUF: $buf\n"; $chan1->close; . .
    You might see if there's anything there that helps.

    Cheers,
    Rob

      I've messed around a lot with blocking and non-blocking connections without much luck, but I'll give your code segment a go.

      Thanks, R

Re: Printing Output From Net::SSH2 exec
by salva (Canon) on May 14, 2009 at 09:18 UTC
    Check Net::OpenSSH, it is far easier to use for this kind of things:
    use Net::OpenSSH; my $ssh = Net::OpenSSH->new($remote_host, user => $remote_user, passwo +rd => $password); $ssh->error and die "Unable to connect to $remote_host: " . $ssh->erro +r; my $fh = $ssh->pipe_out('ls -la /Users/vortura') or die "unable to run command: " . $ssh->error; while (<$fh>) { print "OUT: $_"; } close $fh or die "close failed: $?";
    The module needs a recent version of OpenSSH (4.2 or later, 5.2 recomended) installed on your system and does not work under Windows.
      I hadn't seen that module. I will give it a try. I see it does it's business by running the openssh binary as well, but the multiplexing approach looks a lot nicer than the mess I've inherited.

      Thanks!

        BTW, this is how you read from both stderr and stdout:
        use Net::OpenSSH; my $ssh = Net::OpenSSH->new($host, ...); $ssh->error and die "connection failed: " . $ssh->error; my (undef, $ofh, $efh, $pid) = $ssh->open_ex({ stdout_pipe => 1, stderr_pipe => 1 }, @cmd) or die "ssh command failed: " . $ssh->error; while(1) { # select loop to read from $ofh and $efh without blocking # ... } waitpid($pid, 0); print "result: $?\n";
Re: Printing Output From Net::SSH2 exec
by zentara (Cardinal) on May 14, 2009 at 12:02 UTC
    Whats wrong with shell? Maybe:
    #!/usr/bin/perl use warnings; use strict; use Net::SSH2; use Data::Dumper; # assuming a user named 'z' for demonstration # connecting to localhost, so you need your sshd running # see maillist archives at # http://lists.sourceforge.net/lists/listinfo/ssh-sftp-perl-users # for deeper discussions my $ssh2 = Net::SSH2->new(); $ssh2->connect('localhost') or die "Unable to connect Host $@ \n"; $ssh2->auth_password('z','ztester') or die "Unable to login $@ \n"; #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>; $chan->close; __END__

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku
      $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>;
      That is not reliable!

      The remote shell doesn't signal in any way when the commands run through it exit to its parent process, the ssh server. If the remote command does not run fast enough, the ... while(<$chan>) loop will finish without having read the full output, and the remaining data will be delayed until the next loop (or even later).

      Net::SSH::Expect that uses a similar approach, solves that problem examining the remote output, looking for the shell prompt in order to detect when a subcommand has finished.

        Indeed, I've had more success with shell, but as salva points out, occasionally I end up with partial output. The other thing is that it's hard to distinguish stderr from stdout when running a command under shell.

        It seems like Net::SSH2 has a new maintainer, so hopefully that module will start to see some improvement soon. In the meantime, it looks like Net::OpenSSH is the way to go for me.

        R