Re: [Ssh-sftp-perl-users] quick question with regard to exec, take 2
Brought to you by:
dbrobins
|
From: Charles S B. <cb...@jc...> - 2009-04-06 16:02:40
|
Here is one of my SSH2 testing scripts. It should provide you with the
routines you're after. If not, let me know and I might be able to help
you further. Cut and paste into your editor of choice...
Thanks!
Chuck
e-mail: cb...@jc...
http://www.linkedin.com/in/chuckbrowntx
- - - - - - - - SCRIPT BEGIN - - - - - - - -
#!/usr/bin/perl
########################################################################
#######
# Script Name: test-ssh2.pl
########################################################################
#######
#use warnings;
#use strict;
use Net::SSH2;
use Data::Dumper;
# Version
my $ver = "1.2b";
# Cisco Info
my $host = "hostname_or_IP_here";
my $username = "username_here";
my $password = "password_here";
my $command1 = "show users";
my $command2 = "show ssh";
my $command3 = "show ntp status";
# Display Version
print "VERSION: $ver\n";
# Create new SSH2 object
my $ssh2 = Net::SSH2->new();
# Enable Debug
#$ssh2->debug(1);
# Create a connection to conduct the SSH2 protocol
$ssh2->connect($host) or die "Unable to connect Host $@ \n";
# Determine what type of authentication methods are available
my $authentication_methods = $ssh2->auth_list($username);
print "Valid authentication methods for $host are:
$authentication_methods\n";
# Authenticate using a password
$ssh2->auth_password( $username, $password ) or die "Unable to login $@
\n";
#$ssh2->auth(rank => ['none'],username=>$username) or die "Unable to
login $@ \n";
# Verify we are authenticated - Returns TRUE if authenticated
my $authenticated = $ssh2->auth_ok;
print "Authenticated: $authenticated\n";
# Create a channel object (single channel across our established
connection)
my $chan = $ssh2->channel();
print "DEBUG: After channel\n";
# - - - Un-Comment The Routine You Would Like To Execute - - -
#
# The following subroutines work for Cisco and Alcatel
#
# Execute multiple commands utilizing shell method (method A)
#&use_shell_a( $command1, $command2, $command3 );
# Execute multiple commands utilizing shell method (method B)
#&use_shell_b( $command1, $command2, $command3 );
# Execute multiple commands utilizing shell method (method C)
#&use_shell_c( $command1, $command2, $command3 );
# Execute multiple commands _AND_ pattern match utilizing shell method
(method D)
&use_shell_d( $command1, $command2, $command3 );
#
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
# NOTE: On Solaris 9, you must somehow disconnect from the host or the
perl
# script seems to hang. You can do this several ways.
# 1) least graceful, just disconnect the session.
# $ssh2->disconnect();
#
# 2) more graceful, issue a 'logout' command to your host (or whatever
log-off syntax is required)
# print $chan "logout\n";
# $chan->close;
#
# 3) fancy, check to see if the session has received an eof from the
host. if not, then
# either perform a graceful logout or just disconnect.
# if (! $chan->eof) {
# print "EOF _NOT_ found attempting graceful disconnect.\n";
# print $chan "logout\n";
# }
#
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
# Disconnect from the host
print "DEBUG: Right before disconnect\n";
$ssh2->disconnect();
print "DEBUG: After disconnect\n";
# Exit
exit;
#
# SUBROUTINES
#
sub use_shell_a {
# Start a shell on the remote host
$chan->shell();
# Cycle through each command provided in the sub-routine call
foreach my $command (@_) {
# Print the command to the defined shell
print $chan "$command\n";
# Print the output while there is some data response
print "LINE : $_" while <$chan>;
}
}
sub use_shell_b {
# Start a shell on the remote host
$chan->shell();
# Cycle through each command provided in the sub-routine call
foreach my $command (@_) {
# Print the command to the defined shell
print $chan "$command\n";
# Print the output while there is some data response
while (<$chan>) {
print "RESP($command) : $_";
}
}
}
sub use_shell_c {
# Start a shell on the remote host
$chan->shell();
# Cycle through each command provided in the sub-routine call
foreach my $command (@_) {
# Print the command to the defined shell
print $chan "$command\n";
# Print the output while there is some data response
# Note: This is a more succint version of 'use_shell_b'
print "RESP($command) : $_" while <$chan>;
}
print "complete\n";
}
sub use_shell_d {
# Start a shell on the remote host
$chan->shell();
# Cycle through each command provided in the sub-routine call
foreach my $command (@_) {
# Print the command to the defined shell
print $chan "$command\n";
# While there is some data in $chan
while (<$chan>) {
# Perform a regex match on $_
if (/$username/) {
# If a match was found, print the info
print "I located my username while executing '$command',
matched line:\n";
print "$_\n";
}
}
}
}
sub waitfor {
## waitfor
## If you want to print any console data gathered while waiting:
## @returned_data = &waitfor('searchpattern',10);
## print @returned_data;
## Where 10 is the timeout in seconds...
## Otherwise use:
## &waitfor('searchpattern',10);
##
my ( $search, $timeout ) = @_;
my @lines;
my $PatternFound = undef;
my $TimedOut = undef;
my @formattedoutput = undef;
$timeout += time();
until ( $PatternFound || $TimedOut ) {
# Map some output
@rawoutput = map { $_ } <$chan>;
# Search for pattern and format return output
foreach $rawline (@rawoutput) {
$rawline =~ s/stty: stdin not a tty//g; ## Remove
unimportant error on Symbol
if ( $rawline =~ /$search/ ) {
$PatternFound = 1;
}
while ( $rawline =~ /\s$/ ) { chop $rawline; }
$formattedline = "$rawline<br>";
push( @formattedoutput, $formattedline );
}
# Check for timeout
if ( time() > $timeout ) {
$TimedOut = 1;
}
}
if ($TimedOut) {
print "ERROR: Timed-Out while waiting for '$search'\n";
}
##print "DEBUG: TimedOut = $TimedOut<br>";
##print "DEBUG: PatternFound = $PatternFound<br>";
return (@formattedoutput);
}
sub print_chan {
@output = map {
while ( $_ =~ /\s$/ ) { chop $_; }
$_
} <$chan>;
foreach $line (@output) {
$line =~ s/stty: stdin not a tty//g; ## Remove unimportant
error on Symbol
print "\n$line";
}
}
sub print_chan_continued {
# Will print the output of <$chan> until:
# 1) The maximum number of 'press any key' prompts have been
encountered
# 2) A system command prompt is encountered
my $maxpages = 6;
my $pages = 0;
my $endless_loop = 1;
while ($endless_loop) {
@output = map {
while ( $_ =~ /\s$/ ) { chop $_; }
$_
} <$chan>;
foreach $line (@output) {
$line =~ s/stty: stdin not a tty//g; ## Remove
unimportant error on Symbol
print "\n$line";
if ( $line =~ /Press any key to continue...or/ && $pages <
$maxpages ) {
# Gather another page of data
print $chan "\n";
$pages++;
}
elsif ( $line =~ /Press any key to continue/ && $pages >=
$maxpages ) {
# Printed the maximum number of pages, (q)uit and
return.
print $chan "q\n";
return;
}
elsif ( $line =~ /.*>.*$|Logging out.../ ) {
# Matched regular prompt, return.
##print "DEBUG: matched regular prompt<br>";
return;
}
}
}
}
- - - - - - - - SCRIPT END - - - - - - - -
> -----Original Message-----
> From: Jeremy Kitchen [mailto:ki...@sc...]
> Sent: Thursday, April 02, 2009 12:47 PM
> To: ssh...@li...
> Subject: [Ssh-sftp-perl-users] quick question with regard to exec,
take 2
>
> I'm trying to write a quick program which will execute a series of
> commands on the remote system and get each individual exit status back
> from the commands.
>
> It appears the way to go about doing this is to create a new channel,
> exec, get the exit status, destroy the channel, exec, get the exit
> status, etc.
>
> However, I *also* want to grab the output of these commands (stdout
and
> stderr) in real time, so I'm trying to use poll.
>
> Now, I could be going about this completely the wrong way. If I am,
> please let me know :)
>
> $channel->shell doesn't seem like it fits for me as I don't think I
can
> get the exit statuses back from the remote commands being run.
>
> Here's a bit of what I have so far (which isn't working):
>
> my $channel = $ssh->channel;
>
> $channel->exec("sleep 5 && echo 'moo' && sleep 1");
>
> print "\n";
> while (1) {
> print "loop\n";
> my @poll = ({handle => $channel, events =>
['in','err']});
> if ($ssh->poll(500, \@poll)) {
> # handle events
> }
>
> print Dumper($poll[0]->{revents});
> }
>
>
> everything is all well and good until the command finishes (after the
> sleep 5), when polling completely blocks. Nothing further ever
happens.
> If I kill the remote sshd process handling the connection (the
> user@notty process) then it returns these events:
> $VAR1 = {
> 'in' => 1,
> 'value' => 129,
> 'listener_closed' => 1,
> 'channel_closed' => 1
> };
>
>
> Is there some step I'm missing to make this work how I want?
>
> -Jeremy
|