X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;ds=sidebyside;f=SSH.pm;h=7a421525beb86a87fe98e74eb35e162c8eccb742;hb=a58b44c62073cc5d18789b694b9785333ea4b4f1;hp=3e21be5455585e89779889ae6cb12bb906287edf;hpb=1a7a6ee0eed956c70e77a9246da488ca66341591;p=Net-SSH.git diff --git a/SSH.pm b/SSH.pm index 3e21be5..7a42152 100644 --- a/SSH.pm +++ b/SSH.pm @@ -1,14 +1,19 @@ package Net::SSH; use strict; -use vars qw($VERSION @ISA @EXPORT_OK $ssh); +use vars qw($VERSION @ISA @EXPORT_OK $ssh $equalspace $DEBUG @ssh_options); use Exporter; +use POSIX ":sys_wait_h"; +use IO::File; +use IO::Select; use IPC::Open2; use IPC::Open3; @ISA = qw(Exporter); -@EXPORT_OK = qw( ssh issh sshopen2 sshopen3 ); -$VERSION = '0.02'; +@EXPORT_OK = qw( ssh issh ssh_cmd sshopen2 sshopen3 ); +$VERSION = '0.08'; + +$DEBUG = 0; $ssh = "ssh"; @@ -24,6 +29,15 @@ Net::SSH - Perl extension for secure shell issh('user@hostname', $command); + ssh_cmd('user@hostname', $command); + ssh_cmd( { + user => 'user', + host => 'host.name', + command => 'command', + args => [ '-arg1', '-arg2' ], + stdin_string => "string\n", + } ); + sshopen2('user@hostname', $reader, $writer, $command); sshopen3('user@hostname', $writer, $reader, $error, $command); @@ -32,6 +46,9 @@ Net::SSH - Perl extension for secure shell Simple wrappers around ssh commands. +For an all-perl implementation that does not require the system B command, +see L instead. + =head1 SUBROUTINES =over 4 @@ -44,7 +61,10 @@ Calls ssh in batch mode. sub ssh { my($host, @command) = @_; - my @cmd = ($ssh, '-o', 'BatchMode yes', $host, @command); + @ssh_options = &_ssh_options unless @ssh_options; + my @cmd = ($ssh, @ssh_options, $host, @command); + warn "[Net::SSH::ssh] executing ". join(' ', @cmd). "\n" + if $DEBUG; system(@cmd); } @@ -64,6 +84,77 @@ sub issh { } } +=item ssh_cmd [USER@]HOST, COMMAND [, ARGS ... ] + +=item ssh_cmd OPTIONS_HASHREF + +Calls ssh in batch mode. Throws a fatal error if data occurs on the command's +STDERR. Returns any data from the command's STDOUT. + +If using the hashref-style of passing arguments, possible keys are: + + user (optional) + host (requried) + command (required) + args (optional, arrayref) + stdin_string (optional) - written to the command's STDIN + +=cut + +sub ssh_cmd { + my($host, $stdin_string, @command); + if ( ref($_[0]) ) { + my $opt = shift; + $host = $opt->{host}; + $host = $opt->{user}. '@'. $host if exists $opt->{user}; + @command = ( $opt->{command} ); + push @command, @{ $opt->{args} } if exists $opt->{args}; + $stdin_string = $opt->{stdin_string}; + } else { + ($host, @command) = @_; + undef $stdin_string; + } + + my $reader = IO::File->new(); + my $writer = IO::File->new(); + my $error = IO::File->new(); + + my $pid = sshopen3( $host, $writer, $reader, $error, @command ) or die $!; + + print $writer $stdin_string if defined $stdin_string; + close $writer; + + my $select = new IO::Select; + foreach ( $reader, $error ) { $select->add($_); } + + my($output_stream, $error_stream) = ('', ''); + while ( $select->count ) { + my @handles = $select->can_read; + foreach my $handle ( @handles ) { + my $buffer = ''; + my $bytes = sysread($handle, $buffer, 4096); + if ( !defined($bytes) ) { + waitpid($pid, WNOHANG); + die "[Net::SSH::ssh_cmd] $!" + }; + $select->remove($handle) if !$bytes; + if ( $handle eq $reader ) { + $output_stream .= $buffer; + } elsif ( $handle eq $error ) { + $error_stream .= $buffer; + } + } + + } + + waitpid($pid, WNOHANG); + + die "$error_stream" if length($error_stream); + + return $output_stream; + +} + =item sshopen2 [USER@]HOST, READER, WRITER, COMMAND [, ARGS ... ] Connects the supplied filehandles to the ssh process (in batch mode). @@ -72,7 +163,8 @@ Connects the supplied filehandles to the ssh process (in batch mode). sub sshopen2 { my($host, $reader, $writer, @command) = @_; - open2($reader, $writer, $ssh, '-o', 'Batchmode yes', $host, @command); + @ssh_options = &_ssh_options unless @ssh_options; + open2($reader, $writer, $ssh, @ssh_options, $host, @command); } =item sshopen3 HOST, WRITER, READER, ERROR, COMMAND [, ARGS ... ] @@ -83,7 +175,8 @@ Connects the supplied filehandles to the ssh process (in batch mode). sub sshopen3 { my($host, $writer, $reader, $error, @command) = @_; - open3($writer, $reader, $error, $ssh, '-o', 'Batchmode yes', $host, @command); + @ssh_options = &_ssh_options unless @ssh_options; + open3($writer, $reader, $error, $ssh, @ssh_options, $host, @command); } sub _yesno { @@ -92,6 +185,25 @@ sub _yesno { $x =~ /^y/i; } +sub _ssh_options { + my $reader = IO::File->new(); + my $writer = IO::File->new(); + my $error = IO::File->new(); + open3($writer, $reader, $error, $ssh, '-V'); + my $ssh_version = <$error>; + chomp($ssh_version); + if ( $ssh_version =~ /.*OpenSSH[-|_](\w+)\./ && $1 == 1 ) { + $equalspace = " "; + } else { + $equalspace = "="; + } + my @options = ( '-o', 'BatchMode'.$equalspace.'yes' ); + if ( $ssh_version =~ /.*OpenSSH[-|_](\w+)\./ && $1 > 1 ) { + unshift @options, '-T'; + } + @options; +} + =back =head1 EXAMPLE @@ -125,13 +237,25 @@ Q: My script is "leaking" ssh processes. A: See L, L, L and L. -=head1 AUTHOR +=head1 AUTHORS Ivan Kohler -=head1 CREDITS +John Harrison contributed an example for the documentation. + +Martin Langhoff contributed the ssh_cmd command, and +Jeff Finucane updated it and took care of the 0.04 release. - John Harrison contributed an example for the documentation. +Anthony Awtrey contributed a fix for those still using +OpenSSH v1. + +=head1 COPYRIGHT + +Copyright (c) 2004 Ivan Kohler. +Copyright (c) 2002 Freeside Internet Services, LLC +All rights reserved. +This program is free software; you can redistribute it and/or modify it under +the same terms as Perl itself. =head1 BUGS @@ -141,7 +265,10 @@ Look at IPC::Session (also fsh) =head1 SEE ALSO -ssh-keygen(1), ssh(1), L, L +For an all-perl implementation that does not require the system B command, +see L instead. + +ssh-keygen(1), ssh(1), L, L, L =cut