Dackup

 view release on metacpan or  search on metacpan

lib/Dackup/Target/SSH.pm  view on Meta::CPAN

package Dackup::Target::SSH;
use Moose;
use MooseX::StrictConstructor;
use MooseX::Types::Path::Class;
use Data::Dumper;
use Digest::MD5::File qw(file_md5_hex);
use File::Copy;
use Path::Class;

extends 'Dackup::Target';

has 'ssh' => (
    is       => 'ro',
    isa      => 'Net::OpenSSH',
    required => 1,
);

has 'prefix' => (
    is       => 'ro',
    isa      => 'Path::Class::Dir',
    required => 1,
    coerce   => 1,
);

has 'directories' => (
    is       => 'rw',
    isa      => 'HashRef',
    required => 0,
    default  => sub { {} },
);

__PACKAGE__->meta->make_immutable;

sub entries {
    my $self        = shift;
    my $ssh         = $self->ssh;
    my $dackup      = $self->dackup;
    my $prefix      = $self->prefix;
    my $cache       = $dackup->cache;
    my $directories = $self->directories;

    my ( $type, $type_err )
        = $ssh->capture2(qq{perl -e 'print "directory\\n" if -d "$prefix"'});
    chomp $type;
    return [] if $type ne 'directory';

    my $code = <<'EOF';
#!perl
use strict;
use warnings;
use File::Find;

my $root = 'XXX';
find( \&wanted, $root );

sub wanted {
    my $filename = $File::Find::name;
    my ($dev,  $ino,   $mode,  $nlink, $uid,     $gid, $rdev,
        $size, $atime, $mtime, $ctime, $blksize, $blocks
    ) = stat($filename);
    my $type;
    if ( -f _ ) {
        $type = 'file';
    } elsif ( -d _ ) {
        $type = 'directory';
    } else {
        $type = 'other';
    }
    print "$type:$ctime:$mtime:$size:$ino:$filename\n";
}
EOF
    $code =~ s/XXX/$prefix/;

    my ($tmpnam)
        = $ssh->capture(
        q{perl -e 'use File::Temp qw/:POSIX/; print scalar tmpnam() . "\n"'})
        || die "ssh failed: " . $ssh->error;
    chomp $tmpnam;

    my ( $rin, $in_pid ) = $ssh->pipe_in("cat > $tmpnam")
        or die "pipe_in method failed: " . $ssh->error;
    $rin->print("$code") || die $ssh->error;
    $rin->close || die $ssh->error;
    waitpid( $in_pid, 0 );

    my ($output) = $ssh->capture2("perl $tmpnam")
        || die "ssh failed: " . $ssh->error;
    $ssh->system("rm $tmpnam")
        or die "remote command failed: " . $ssh->error;
    return [] unless $output;

    my @entries;
    my @not_in_cache;
    foreach my $line ( split "\n", $output ) {
        my ( $type, $ctime, $mtime, $size, $inodenum, $filename ) = split ':',
            $line, 6;
        next if $type eq 'other';
        confess "Error with stat: $line"
            unless $type
                && defined($filename)
                && $ctime
                && $mtime
                && defined($size)
                && defined($inodenum);

        if ( $type eq 'directory' ) {
            $directories->{$filename} = 1;
            next;
        }

        my $key = file($filename)->relative($prefix)->stringify;
        my $cachekey
            = 'ssh:' . $ssh->{_user} . ':' . $ssh->{_host} . ':' . $line;

        my $md5_hex = $cache->get($cachekey);
        if ($md5_hex) {
            push @entries,
                Dackup::Entry->new(



( run in 0.757 second using v1.01-cache-2.11-cpan-5735350b133 )