+=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;
+
+}
+