Home > Blockchain >  Net::OpenSSH; rsync_put writes progress into file, scp_put doesn't
Net::OpenSSH; rsync_put writes progress into file, scp_put doesn't

Time:03-29

rysnc_put and scp_put both show the progress in the terminal. But when i try to write it into a file i can only see the progress from rysnc_put. The problem remains even if i just print the output from the pty [my edit]. Is this a problem with my pty usage? It is only mentioned for scp_put, that there is no progress when the STDOUT is not a tty.

Note that scp will not generate progress reports unless its stdout stream is attached to a tty.

#!/usr/bin/perl
use strict;
use warnings;
use Net::OpenSSH;

my $SSH = Net::OpenSSH->new(
    '192.168.178.33',
    (
        user => 'TT',
        password => 'TTpassword',
        port => '22',
    )
);

my $pty = new IO::Pty;
my $slave = $pty->slave;

$SSH->scp_put(
    {
        quiet => 0,
        verbose => 1,
        stdout_fh => $pty,
        stderr_fh => $pty,
        bwlimit => '200',
    },
    '/opt/TT/CHANGES.md',
    '/opt/TT/CHANGES.md',,
);

while (<$pty>) {
    print STDOUT "$_";
}

CodePudding user response:

Even if scp_put and rsync_put are similar methods, they wrap two unrelated programs (scp and rsync) which regarding that matter, do not behave in the same way.

Specifically, scp checks whether its stdio streams are attached to a pty and when not, it suppresses progress messages. AFAIK that feature can not be disabled.

Update: Reading from the PTY in parallel:

This code reads from the PTY and writes the data to a file in parallel:

use strict;
use warnings;

use Net::OpenSSH;
use POSIX ();
use IO::Pty;

my $ssh = Net::OpenSSH->new("localhost"); #, scp_cmd => "/home/salva/t/openssh-8.9p1/scp");

my $pty = new IO::Pty;
my $slave = $pty->slave;
print "slave: $slave, fd: ".fileno($slave)."\n";

my $logger_pid = fork;
unless ($logger_pid) {
  defined $logger_pid or die "fork failed: $!";

  open my $log, ">", "/tmp/scp.log";
  select $log;
  $| = 1;

  print("Reading from PTY\n");
  while (1) {
      my $buffer;
      sysread($pty, $buffer, 1);
      syswrite($log, $buffer, 1);
  }
  print "PTY closed unexpectedly\n";
  POSIX::_exit();
}


my $scp_pid = fork;
unless($scp_pid) {
    defined $scp_pid or die "fork failed: $!";

    $pty->make_slave_controlling_terminal();
    $ssh->scp_put({stdout_fh => $slave,
                   stderr_fh => $slave,
                   quiet => 0},
                  "/etc/passwd", "/tmp/foo.vdi");
}

waitpid($scp_pid, 0);

kill 9, $logger_pid;
waitpid($logger_pid, 0);

CodePudding user response:

Another solution to your problem is to customize the pure perl implementation of the SCP protocol available from Net::SSH::Any.

I designed the SCP classes from that package to be subclassed so that things like that could be done but never went to actually document that API, so, well, you would be relying on undocumented features (though, I don't plan to change it).

use strict;
use warnings;

package MyPutter {
    use parent 'Net::SSH::Any::SCP::Putter::Standard';

    use Data::Dumper qw(Dumper);

    for my $name (qw(open_dir open_file close_file close_dir)) {
        my $method = $name;
        my $super = Net::SSH::Any::SCP::Putter::Standard->can($name);
        no strict 'refs';
        *$name = sub {
            my ($putter, $a) = @_;
            print STDERR "$method $a->{path} --> $a->{local_path}\n";
            $super->(@_);
        };
    }

    sub read_file {
        my $putter = shift;
        my $a = shift;
        my $data = $putter->SUPER::read_file($a, @_);
        $a->{bytes_copied}  = length($data);
        print STDERR "read_file $a->{path} --> $a->{local_path} $a->{bytes_copied}/$a->{size}\n";
        return $data;
    }
};

use Net::OpenSSH;
my $ssh = Net::OpenSSH->new("localhost");

my $any = $ssh->any;
my $putter = MyPutter->_new($any, {}, "/tmp/out2", "/tmp/passwd3");
$putter->run({})
  • Related