-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrsync_wrapper.pl
More file actions
471 lines (375 loc) · 15.3 KB
/
rsync_wrapper.pl
File metadata and controls
471 lines (375 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
# __
# /\ \ 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;