mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-05-30 09:47:00 -04:00
Fold the standalone rsync-web repo into the rsync source tree as rsync-web/, eliminating the sibling-checkout convention and the drift it causes between the release-time HTML snapshot in ../release/rsync-html and the source of truth in ../rsync-web. Flat-copy import (no git history merge). The standalone repo at github.com/RsyncProject/rsync-web is retained for historical reference and will be archived once the in-tree copy proves itself. Add /rsync-web/ to .gitattributes with export-ignore so the website content does not bloat the release source tarball produced by 'git archive' in packaging/release.py step_7_tarball. A follow-up commit repoints HTML_SRC in packaging/release.py at the new in-tree location.
472 lines
15 KiB
Perl
472 lines
15 KiB
Perl
# __
|
|
# /\ \ From the mind of
|
|
# / \ \
|
|
# / /\ \ \_____ Lee Eakin <LEakin@Nostrum.COM>
|
|
# / \ \ \______\ or <Lee@Eakin.ORG>
|
|
# / /\ \ \/____ /
|
|
# \ \ \ \____\/ / Wrapper module for the rsync program
|
|
# \ \ \/____ / rsync can be found at http://rsync.samba.org/rsync/
|
|
# \ \____\/ /
|
|
# \/______/
|
|
|
|
package Rsync;
|
|
require 5.004;
|
|
|
|
use FileHandle;
|
|
use IPC::Open3 qw(open3);
|
|
use Carp;
|
|
|
|
use strict;
|
|
|
|
=head1 NAME
|
|
|
|
Rsync - perl module interface to B<rsync> http://rsync.samba.org/rsync/
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use Rsync;
|
|
|
|
$obj = Rsync->new(qw(-az C<-e> /usr/local/bin/ssh
|
|
--rsync-path /usr/local/bin/rsync));
|
|
|
|
$obj->exec(qw(localdir rhost:remdir))
|
|
or warn "rsync failed\n";
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Perl Convenience wrapper for B<rsync> program. Written for B<rsync> 2.3.1 but
|
|
should perform properly with most versions.
|
|
|
|
=cut
|
|
|
|
# options from the rsync man pae
|
|
###### Boolean flags ######
|
|
# -h, --help show this help screen
|
|
# -v, --verbose increase verbosity
|
|
# -q, --quiet decrease verbosity
|
|
# -c, --checksum always checksum
|
|
# -a, --archive archive mode
|
|
# -r, --recursive recurse into directories
|
|
# -R, --relative use relative path names
|
|
# -b, --backup make backups (default ~ suffix)
|
|
# -u, --update update only (don't overwrite newer files)
|
|
# -l, --links preserve soft links
|
|
# -L, --copy-links treat soft links like regular files
|
|
# --copy-unsafe-links copy links outside the source tree
|
|
# --safe-links ignore links outside the destination tree
|
|
# -H, --hard-links preserve hard links
|
|
# -p, --perms preserve permissions
|
|
# -o, --owner preserve owner (root only)
|
|
# -g, --group preserve group
|
|
# -D, --devices preserve devices (root only)
|
|
# -t, --times preserve times
|
|
# -S, --sparse handle sparse files efficiently
|
|
# -n, --dry-run show what would have been transferred
|
|
# -W, --whole-file copy whole files, no incremental checks
|
|
# -x, --one-file-system don't cross filesystem boundaries
|
|
# -C, --cvs-exclude auto ignore files in the same way CVS does
|
|
# RCS SCCS CVS CVS.adm RCSLOG cvslog.* tags TAGS
|
|
# .make.state .nse_depinfo *~ #* .#* ,* *.old *.bak
|
|
# *.BAK *.orig *.rej .del-* *.a *.o *.obj *.so *.Z
|
|
# *.elc *.ln core
|
|
# --delete delete files that don't exist on the sending side
|
|
# --delete-excluded also delete excluded files on the receiving side
|
|
# --partial keep partially transferred files
|
|
# --force force deletion of directories even if not empty
|
|
# --numeric-ids don't map uid/gid values by user/group name
|
|
# -I, --ignore-times don't exclude files that match length and time
|
|
# --size-only only use file size when determining if a file
|
|
# should be transferred
|
|
# -z, --compress compress file data
|
|
# --version print version number
|
|
# --daemon run as a rsync daemon
|
|
# --stats give some file transfer stats
|
|
# --progress show progress during transfer
|
|
###### scalar values ######
|
|
# --csum-length=LENGTH <=16 bit md4 checksum size
|
|
# -B, --block-size=SIZE checksum blocking size (default 700)
|
|
# --timeout=TIME set IO timeout in seconds
|
|
# --port=PORT specify alternate rsyncd port number
|
|
# -e, --rsh=COMMAND specify rsh replacement
|
|
# -T, --temp-dir=DIR create temporary files in directory DIR
|
|
# --compare-dest=DIR also compare destination files relative to DIR
|
|
# --exclude-from=FILE exclude patterns listed in FILE
|
|
# --include-from=FILE don't exclude patterns listed in FILE
|
|
# --config=FILE specify alternate rsyncd.conf file
|
|
# --password-file=FILE get password from FILE
|
|
# --log-format=FORMAT log file transfers using specified format
|
|
# --suffix=SUFFIX override backup suffix
|
|
# --rsync-path=PATH specify path to rsync on the remote machine
|
|
###### array values ######
|
|
# --exclude=PATTERN exclude files matching PATTERN
|
|
# --include=PATTERN don't exclude files matching PATTERN
|
|
#
|
|
|
|
=over 4
|
|
|
|
=item Rsync::new
|
|
|
|
$obj = new Rsync;
|
|
|
|
or
|
|
|
|
$obj = Rsync->new;
|
|
|
|
or
|
|
|
|
$obj = Rsync->new(@options);
|
|
|
|
=back
|
|
|
|
Create an Rsync object. Any options passed at creation are stored in
|
|
the object as defaults for all future exec call on that object. Options
|
|
are the same as those in L<rsync> with the addition of
|
|
--path-to-rsync which can be used to override the hardcoded default of
|
|
/usr/local/bin/rsync, and --debug which causes the module functions to
|
|
print some debugging information to STDERR.
|
|
|
|
=cut
|
|
|
|
sub new {
|
|
my $class=shift;
|
|
|
|
# seed the options hash, booleans, scalars, excludes, data,
|
|
# status, stderr/stdout storage for last exec
|
|
my $self={
|
|
# the full path name to the rsync binary
|
|
'path-to-rsync' => '/usr/local/bin/rsync',
|
|
# these are the boolean flags to rsync, all default off, including them
|
|
# in the args list turns them on
|
|
'flag' => {qw(
|
|
--archive 0 --backup 0 --checksum 0
|
|
--compress 0 --copy-links 0 --copy-unsafe-links 0
|
|
--cvs-exclude 0 --daemon 0 --delete 0
|
|
--delete-excluded 0 --devices 0 --dry-run 0
|
|
--force 0 --group 0 --hard-links 0
|
|
--help 0 --ignore-times 0 --links 0
|
|
--numeric-ids 0 --one-file-system 0 --owner 0
|
|
--partial 0 --perms 0 --progress 0
|
|
--quiet 0 --recursive 0 --relative 0
|
|
--safe-links 0 --size-only 0 --sparse 0
|
|
--stats 0 --times 0 --update 0
|
|
--verbose 0 --version 0 --whole-file 0
|
|
)},
|
|
# these have simple scalar args we cannot easily check
|
|
'scalar' => {qw(
|
|
--block-size 0 --compare-dest 0 --config 0
|
|
--csum-length 0 --exclude-from 0 --include-from 0
|
|
--log-format 0 --password-file 0 --port 0
|
|
--rsh 0 --rsync-path 0 --suffix 0
|
|
--temp-dir 0 --timeout 0
|
|
)},
|
|
# these can be specified multiple times and are additive, the doc also
|
|
# specifies that it is an ordered list so we must preserve that order
|
|
'exclude' => [],
|
|
# source/destination path names and hostnames
|
|
'data' => [],
|
|
# return status from last exec
|
|
'status' => 0,
|
|
'realstatus' => 0,
|
|
# whether or not to print debug statements
|
|
'debug' => 0,
|
|
# stderr from last exec in array format (messages from remote rsync proc)
|
|
'err' => [],
|
|
# stdout from last exec in array format (messages from local rsync proc)
|
|
'out' => [],
|
|
};
|
|
if (@_) {
|
|
&defopts($self,@_) or return undef;
|
|
}
|
|
bless $self, $class;
|
|
}
|
|
|
|
=over 4
|
|
|
|
=item Rsync::defopts
|
|
|
|
defopts $obj @options;
|
|
|
|
or
|
|
|
|
$obj->defopts(@options);
|
|
|
|
=back
|
|
|
|
Set default options for future exec calls for the object. See L<rsync>
|
|
for a complete list of valid options. This is really the internal
|
|
function that B<new> calls but you can use it too. Presently there is no way
|
|
to turn off the boolean options short of creating another object, but if it is
|
|
needed and the B<rsync> guys don't use it, I may add hooks to let + and ++ or a
|
|
leading no- toggle it back off similar to B<Getopt::Long> (the GNU way).
|
|
|
|
=cut
|
|
|
|
sub defopts {
|
|
my $self=shift;
|
|
my @opts=@_;
|
|
|
|
# need a conversion table in case someone uses the short options
|
|
my %short=(qw(
|
|
-B --block-size -C --cvs-exclude -D --devices -H --hard-links
|
|
-I --ignore-times -L --copy-links -R --relative -T --temp-dir
|
|
-W --whole-file -a --archive -b --backup -c --checksum
|
|
-e --rsh -g --group -h --help -l --links
|
|
-n --dry-run -o --owner -p --perms -q --quiet
|
|
-r --recursive -s --sparse -t --times -u --update
|
|
-v --verbose -x --one-file-system -z --compress
|
|
));
|
|
while (my $opt=shift @opts) {
|
|
my $arg;
|
|
print(STDERR "setting debug flag\n"),$self->{debug}=1,next
|
|
if $opt eq '--debug';
|
|
print STDERR "processing option: $opt\n" if $self->{debug};
|
|
if ($opt=~/^-/) {
|
|
# handle short opts first
|
|
if ($opt=~/^-(\w+)$/) {
|
|
foreach (split '',$1) {
|
|
print STDERR "short option: -$_\n" if $self->{debug};
|
|
if (exists $short{'-'.$_}) {
|
|
$opt=$short{'-'.$_};
|
|
# convert it to the long form
|
|
$opt=$short{$opt} if exists $short{$opt};
|
|
# handle the 3 short opts that require args
|
|
$self->{scalar}{$opt}=shift(@opts),next if (/^[BeT]$/);
|
|
# handle the rest
|
|
$self->{flag}{$opt}=1,next if exists $self->{flag}{$opt};
|
|
}
|
|
carp "$opt - unknown option\n";
|
|
return undef;
|
|
}
|
|
}
|
|
# handle long opts with = args
|
|
if ($opt=~/^(--\w+[\w-]*)=(.*)$/) {
|
|
print STDERR "splitting longopt: $opt ($1 $2)\n" if $self->{debug};
|
|
($opt,$arg)=($1,$2);
|
|
}
|
|
# handle boolean flags
|
|
$self->{flag}{$opt}=1,next if exists $self->{flag}{$opt};
|
|
# handle simple scalars
|
|
$self->{scalar}{$opt}=($arg || shift @opts),next
|
|
if exists $self->{scalar}{$opt};
|
|
# handle excludes
|
|
if ($opt eq '--exclude') {
|
|
$arg||=shift @opts;
|
|
# if they sent a reset, we will too
|
|
$self->{exclude}=[],next if $arg eq '!';
|
|
# otherwise add it to the list
|
|
push @{$self->{exclude}},$arg;
|
|
next;
|
|
}
|
|
# to preserve order we store both includes and excludes in the same
|
|
# array. We use the leading '+ ' (plus space) trick from the man
|
|
# page to accomplish this.
|
|
if ($opt eq '--include') {
|
|
$arg||=shift @opts;
|
|
# first check to see if this is really an exclude
|
|
push(@{$self->{exclude}},$arg),next if $arg=~s/^- //;
|
|
# next see if they sent a reset, if they did, we will too
|
|
$self->{exclude}=[],next if $arg eq '!';
|
|
# if it really is an include, fix it first, since we use exclude
|
|
$arg='+ '.$arg unless $arg=~/^\+ /;
|
|
push @{$self->{exclude}},$arg;
|
|
next;
|
|
}
|
|
# handle our special case to override hard-coded path to rsync
|
|
$self->{'path-to-rsync'}=($arg || shift @opts),next
|
|
if $opt eq '--path-to-rsync';
|
|
# if we get this far nothing matched so it must be an error
|
|
carp "$opt - unknown option\n";
|
|
return undef;
|
|
} else { # must be data (source/destination info)
|
|
print STDERR "adding to data array: $opt\n" if $self->{debug};
|
|
push(@{$self->{data}},$opt);
|
|
}
|
|
}
|
|
1;
|
|
}
|
|
|
|
=over 4
|
|
|
|
=item Rsunc::exec
|
|
|
|
exec $obj @options or warn "rsync failed\n";
|
|
|
|
or
|
|
|
|
$obj->exec(@options) or warn "rsync failed\n";
|
|
|
|
=back
|
|
|
|
This is the function that does the real work. Any options passed to this
|
|
routine are appended to any pre-set options and are not saved. They effect
|
|
the current execution of B<rsync> only. It returns 1 if the return status was
|
|
zero (or true), if the B<rsync> return status was non-zero it returns undef and
|
|
stores the return status. You can examine the return status from rsync and
|
|
any output to stdout and stderr with the functions listed below.
|
|
|
|
=cut
|
|
|
|
sub exec {
|
|
my $self=shift;
|
|
my @cmd=($self->{'path-to-rsync'});
|
|
|
|
foreach (sort keys %{$self->{flag}}) {
|
|
push @cmd,$_ if $self->{flag}{$_};
|
|
}
|
|
foreach (sort keys %{$self->{scalar}}) {
|
|
push @cmd,$_.'='.$self->{scalar}{$_} if $self->{scalar}{$_};
|
|
}
|
|
foreach (@{$self->{exclude}}) {
|
|
push @cmd,'--exclude='.$_;
|
|
}
|
|
foreach (@{$self->{data}}) {
|
|
push @cmd,$_;
|
|
}
|
|
push @cmd,@_ if @_;
|
|
print STDERR "exec: @cmd\n" if $self->{debug};
|
|
my $in=FileHandle->new; my $out=FileHandle->new; my $err=FileHandle->new;
|
|
my $pid=eval{ open3 $in,$out,$err,@cmd };
|
|
if ($@) {
|
|
$self->{realstatus}=0;
|
|
$self->{status}=255;
|
|
$self->{err}=[$@,"Execution of rsync failed.\n"];
|
|
return undef;
|
|
}
|
|
$in->close; # we don't use it and neither should rsync (at least not yet)
|
|
$self->{err}=[ $err->getlines ];
|
|
$self->{out}=[ $out->getlines ];
|
|
$err->close;
|
|
$out->close;
|
|
waitpid $pid,0;
|
|
$self->{realstatus}=$?;
|
|
$self->{status}=$?>>8;
|
|
return undef if $self->{status};
|
|
return 1;
|
|
}
|
|
|
|
=over 4
|
|
|
|
=item Rsync::status
|
|
|
|
$rval = status $obj;
|
|
|
|
or
|
|
|
|
$rval = $obj->status;
|
|
|
|
=back
|
|
|
|
Returns the status from last B<exec> call right shifted 8 bits.
|
|
|
|
=cut
|
|
|
|
sub status {
|
|
my $self=shift;
|
|
return $self->{status};
|
|
}
|
|
|
|
=over 4
|
|
|
|
=item Rsync::realstatus
|
|
|
|
$rval = realstatus $obj;
|
|
|
|
or
|
|
|
|
$rval = $obj->realstatus;
|
|
|
|
=back
|
|
|
|
Returns the real status from last B<exec> call (not right shifted).
|
|
|
|
=cut
|
|
|
|
sub realstatus {
|
|
my $self=shift;
|
|
return $self->{realstatus};
|
|
}
|
|
|
|
=over 4
|
|
|
|
=item Rsync::err
|
|
|
|
$aref = err $obj;
|
|
|
|
or
|
|
|
|
$aref = $obj->err;
|
|
|
|
=back
|
|
|
|
Returns an array or a reference to the array containing all output to stderr
|
|
from the last B<exec> call. B<rsync> sends all messages from the remote
|
|
B<rsync> process to stderr. This functions purpose is to make it easier for
|
|
you to parse that output for appropriate information.
|
|
|
|
=cut
|
|
|
|
sub err {
|
|
my $self=shift;
|
|
return(wantarray ? @{$self->{err}} : $self->{err});
|
|
}
|
|
|
|
=over 4
|
|
|
|
=item Rsync::out
|
|
|
|
$aref = out $obj;
|
|
|
|
or
|
|
|
|
$aref = $obj->out;
|
|
|
|
=back
|
|
|
|
Similar to the B<err> function, this returns an array or a reference to the
|
|
array containing all output to stdout from the last B<exec> call. B<rsync>
|
|
sends all messages from the local B<rsync> process to stdout.
|
|
|
|
=cut
|
|
|
|
sub out {
|
|
my $self=shift;
|
|
return(wantarray ? @{$self->{out}} : $self->{out});
|
|
}
|
|
|
|
=head1 Author
|
|
|
|
Lee Eakin E<lt>leakin@nostrum.comE<gt>
|
|
|
|
=head1 Credits
|
|
|
|
Gerard Hickey C<PGP::Pipe>
|
|
|
|
Russ Allbery C<PGP::Sign>
|
|
|
|
Graham Barr C<Net::*>
|
|
|
|
Andrew Tridgell and Paul Mackerras C<rsync(1)>
|
|
|
|
John Steele E<lt>steele@nostrum.comE<gt>
|
|
|
|
Philip Kizer E<lt>pckizer@nostrum.comE<gt>
|
|
|
|
Larry Wall C<perl(1)>
|
|
|
|
I borrowed many clues on wrapping an external program from the PGP modules,
|
|
and I would not have had such a useful tool to wrap except for the great work
|
|
of the B<rsync> authors. Thanks also to Graham Barr, the author of the libnet
|
|
modules and many others, for looking over this code. Of course I must mention
|
|
the other half of my brain, John Steele, and his good friend Philip Kizer for
|
|
finding B<rsync> and bringing it to my attention. And I would not have been
|
|
able to enjoy writing useful tools if not for the creator of the B<perl>
|
|
language.
|
|
|
|
=head1 Copyrights
|
|
|
|
Copyleft (l) 1999, by Lee Eakin
|
|
|
|
=cut
|
|
|
|
1;
|