File-RsyncP
view release on metacpan or search on metacpan
lib/File/RsyncP.pm view on Meta::CPAN
#
# See http://perlrsync.sourceforge.net.
#
#========================================================================
package File::RsyncP;
use strict;
use Socket;
use File::RsyncP::Digest;
use File::RsyncP::FileIO;
use File::RsyncP::FileList;
use Getopt::Long;
use Data::Dumper;
use Config;
use Encode qw/from_to/;
use Fcntl;
use vars qw($VERSION);
$VERSION = '0.76';
use constant S_IFMT => 0170000; # type of file
use constant S_IFDIR => 0040000; # directory
use constant S_IFCHR => 0020000; # character special
use constant S_IFBLK => 0060000; # block special
use constant S_IFREG => 0100000; # regular
use constant S_IFLNK => 0120000; # symbolic link
use constant S_IFSOCK => 0140000; # socket
use constant S_IFIFO => 0010000; # fifo
sub new
{
my($class, $options) = @_;
$options ||= {};
my $rs = bless {
protocol_version => 28,
logHandler => \&logHandler,
abort => 0,
%$options,
}, $class;
#
# In recent versions of rsync (eg: 2.6.8) --devices is no
# longer identical to -D. Now -D means --devices --specials.
# File::RsyncP assumes --devices behaves the same as -D,
# and doesn't currently handle --specials.
#
# To make sure we don't lie to the remote rsync, we must
# send -D instead of --devices. Therefore, we manually
# replace --devices with -D in $rs->{rsyncArgs}.
#
for ( my $i = 0 ; $i < @{$rs->{rsyncArgs}} ; $i++ ) {
$rs->{rsyncArgs}[$i] = "-D"
if ( $rs->{rsyncArgs}[$i] eq "--devices" );
}
#
# process rsync options
#
local(@ARGV);
$rs->{rsyncOpts} = {};
@ARGV = @{$rs->{rsyncArgs}};
my $p = new Getopt::Long::Parser(
config => ["bundling", "pass_through"],
);
#
# First extract all the exclude related options for processing later
#
return if ( !$p->getoptions(
"exclude=s", sub { optExclude($rs, @_); },
"exclude-from=s", sub { optExclude($rs, @_); },
"include=s", sub { optExclude($rs, @_); },
"include-from=s", sub { optExclude($rs, @_); },
"cvs-exclude|C", sub { optExclude($rs, @_); },
) );
#
# Since the exclude arguments are no longer needed (they are
# passed via the socket, not the command-line args), update
# $rs->{rsyncOpts}
#
@{$rs->{rsyncArgs}} = @ARGV;
#
# Now process the rest of the arguments we care about
#
return if ( !$p->getoptions($rs->{rsyncOpts},
"block-size=i",
"devices|D",
"from0|0",
"group|g",
"hard-links|H",
"ignore-times|I",
"links|l",
"numeric-ids",
"owner|o",
"perms|p",
"protocol=i",
"recursive|r",
"relative|R",
"timeout",
"verbose|v+",
) );
$rs->{blockSize} = $rs->{rsyncOpts}{"block-size"};
$rs->{timeout} ||= $rs->{rsyncOpts}{timeout};
$rs->{protocol_version} = $rs->{rsyncOpts}{protocol}
if ( defined($rs->{rsyncOpts}{protocol}) );
$rs->{fio_version} = 1;
if ( !defined($rs->{fio}) ) {
$rs->{fio} = File::RsyncP::FileIO->new({
blockSize => $rs->{blockSize},
logLevel => $rs->{logLevel},
protocol_version => $rs->{protocol_version},
preserve_hard_links => $rs->{rsyncOpts}{"hard-links"},
clientCharset => $rs->{clientCharset},
});
eval { $rs->{fio_version} = $rs->{fio}->version; };
} else {
#
# Tell the existing FileIO module various parameters that
# depend upon the parsed rsync args
#
eval { $rs->{fio_version} = $rs->{fio}->version; };
$rs->{fio}->blockSize($rs->{blockSize});
if ( $rs->{fio_version} >= 2 ) {
$rs->{fio}->protocol_version($rs->{protocol_version});
$rs->{fio}->preserve_hard_links($rs->{rsyncOpts}{"hard-links"});
} else {
#
# old version of FileIO: only supports version 26
#
$rs->{protocol_version} = 26 if ( $rs->{protocol_version} > 26 );
}
}
#
# build signal list in case we do an abort
#
my $i = 0;
foreach my $name ( split(' ', $Config{sig_name}) ) {
$rs->{sigName2Num}{$name} = $i;
$i++;
}
return $rs;
}
sub optExclude
{
my($rs, $argName, $argValue) = @_;
push(@{$rs->{excludeArgs}}, {name => $argName, value => $argValue});
}
#
# Strip the exclude and include arguments from the given argument list
#
sub excludeStrip
{
my($rs, $args) = @_;
local(@ARGV);
my $p = new Getopt::Long::Parser(
config => ["bundling", "pass_through"],
);
@ARGV = @$args;
#
# Extract all the exclude related options
#
$p->getoptions(
"exclude=s", sub { },
"exclude-from=s", sub { },
"include=s", sub { },
"include-from=s", sub { },
"cvs-exclude|C", sub { },
);
return \@ARGV;
}
sub serverConnect
{
my($rs, $host, $port) = @_;
#local(*FH);
$port ||= 873;
my $proto = getprotobyname('tcp');
my $iaddr = inet_aton($host) || return "unknown host $host";
my $paddr = sockaddr_in($port, $iaddr);
alarm($rs->{timeout}) if ( $rs->{timeout} );
socket(FH, PF_INET, SOCK_STREAM, $proto)
|| return "inet socket: $!";
connect(FH, $paddr) || return "inet connect: $!";
$rs->{fh} = *FH;
$rs->writeData("\@RSYNCD: $rs->{protocol_version}\n", 1);
my $line = $rs->getLine;
alarm(0) if ( $rs->{timeout} );
if ( $line !~ /\@RSYNCD:\s*(\d+)/ ) {
return "unexpected response $line\n";
}
$rs->{remote_protocol} = $1;
if ( $rs->{remote_protocol} < 20 || $rs->{remote_protocol} > 40 ) {
return "Bad protocol version: $rs->{remote_protocol}\n";
}
$rs->log("Connected to $host:$port, remote version $rs->{remote_protocol}")
if ( $rs->{logLevel} >= 1 );
$rs->{protocol_version} = $rs->{remote_protocol}
if ( $rs->{protocol_version} > $rs->{remote_protocol} );
$rs->{fio}->protocol_version($rs->{protocol_version})
if ( $rs->{fio_version} >= 2 );
$rs->log("Negotiated protocol version $rs->{protocol_version}")
if ( $rs->{logLevel} >= 1 );
return;
}
sub serverList
{
my($rs) = @_;
my(@service);
( run in 1.219 second using v1.01-cache-2.11-cpan-a9496e3eb41 )