File Coverage

blib/lib/File/Remote.pm
Criterion Covered Total %
statement 28 284 9.8
branch 0 230 0.0
condition 0 74 0.0
subroutine 9 37 24.3
pod 22 22 100.0
total 59 647 9.1


line stmt bran cond sub pod time code
1              
2             # $Id: Remote.pm,v 1.17 2005/01/10 21:47:52 nwiger Exp $
3             ####################################################################
4             #
5             # Copyright (c) 1998-2003 Nathan Wiger
6             #
7             # This module takes care of dealing with files regardless of whether
8             # they're local or remote. It allows you to create and edit files
9             # without having to worry about their physical location. If a file
10             # passed in is of the form 'host:/path/to/file', then it uses rsh/rcp
11             # or ssh/scp (depending on how you configure it) calls to edit the file
12             # remotely. Otherwise, it edits the file locally.
13             #
14             # It is my intent to provide a full set of File::Remote routines that
15             # mirror the standard file routines. If anybody notices any that are
16             # missing or even has some suggestions for useful ones, I'm all ears.
17             #
18             # For full documentation, use "perldoc Remote.pm" or "man File::Remote"
19             #
20             # This module is free software; you may copy this under the terms of
21             # the GNU General Public License, or the Artistic License, copies of
22             # which should have accompanied your Perl kit.
23             #
24             ####################################################################
25              
26             #=========================== Setup =================================
27              
28             # Basic module setup
29             require 5.005;
30             package File::Remote;
31              
32 1     1   6053 use strict;
  1         2  
  1         40  
33 1         111 use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS $VERSION
34 1     1   7 %RW_HANDLES %RO_HANDLES %RW_TMPFILES %RO_TMPFILES);
  1         3  
35 1     1   37 use Exporter;
  1         12  
  1         198  
36             @ISA = qw(Exporter);
37              
38             @EXPORT_OK = qw(
39             rreadfile rwritefile rmkdir rrmdir rrm runlink rcp rcopy rtouch rchown
40             rchmod rmove rmv rbackup setrsh setrcp settmp ropen rclose rappend rprepend
41             rsymlink rlink readfile writefile mkdir rmdir rm unlink cp copy touch chown
42             chmod move mv backup open close append prepend symlink link readlink rreadlink
43             );
44              
45             %EXPORT_TAGS = (
46             files => [qw(ropen rclose rreadfile rwritefile runlink rcopy rtouch rmove
47             rbackup rappend rprepend rlink rsymlink rreadlink)],
48             config => [qw(setrsh setrcp settmp)],
49             dirs => [qw(rmkdir rrmdir)],
50             perms => [qw(rchown rchmod)],
51             standard => [qw(ropen rclose rreadfile rwritefile runlink rcopy rtouch rmove
52             rbackup rappend rprepend setrsh setrcp settmp rmkdir rrmdir
53             rchown rchmod rsymlink rlink rreadlink)],
54             aliases => [qw(rrm rmv rcp)],
55             replace => [qw(open close readfile writefile unlink rm copy cp touch move mv
56             backup append prepend setrsh setrcp settmp mkdir rmdir chown chmod
57             symlink link readlink)]
58             );
59              
60             # Straight from CPAN
61             $VERSION = do { my @r=(q$Revision: 1.17 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r };
62              
63             # Errors
64 1     1   11 use Carp;
  1         2  
  1         62  
65              
66             # Need the basic File classes to make it work
67 1     1   924 use File::Copy qw(!copy !move); # prevent namespace clashes
  1         5339  
  1         128  
68 1     1   6 use File::Path;
  1         2  
  1         53  
69              
70             # For determining remote or local file
71 1     1   684 use Sys::Hostname;
  1         1014  
  1         627  
72              
73             #======================== Configuration ==========================
74              
75             # Defaults
76             my @OPT = (
77             rsh => "/usr/bin/rsh",
78             rcp => "/usr/bin/rcp",
79             tmp => "/tmp"
80             );
81              
82             # This determines whether or not we should spend some time trying
83             # to see if rsh and rcp are set to valid values before using them.
84             # By default these checks are not done because they're SLOW...
85             # Note that if you enable these then you must use absolute paths
86             # when calling setrsh and setrcp; "setrsh('ssh')" will fail.
87             my $CHECK_RSH_IS_VALID = 0;
88             my $CHECK_RCP_IS_VALID = 0;
89              
90             # This is whether or not to spend the extra cycles (and network
91             # latency) checking whether a remote file is actually writeable
92             # when we try to open it with > or >>. Note: Unsetting this can
93             # result in strange and unpredictable behavior, messing with it
94             # is NOT recommended.
95             my $CHECK_REMOTE_FILES = 1;
96              
97             #======================== Misc. Settings =========================
98              
99             # This is the default class for the File::Remote object (from CGI.pm!)
100             my $DefaultClass ||= 'File::Remote';
101             my $DefaultClassObject; # holds an object later on
102              
103             # This should not need to be overridden
104             (my $hostname = hostname()) =~ s/\..*//;
105              
106             # Need to check our OS. As of this release, only UNIX is supported;
107             # perhaps this will change in the future, but probably not.
108             # Don't check $^O because we'd have to write an exhaustive function.
109             die "Sorry, File::Remote only supports UNIX systems\n" unless (-d "/");
110              
111             #========================== Functions ============================
112              
113             # Simple debugging function
114             my $DEBUG = 0;
115 0 0   0     sub _debug { warn "debug: ", @_ if $DEBUG };
116              
117             #------------------------------------------------
118             # "Constructor" function to handle defaults
119             #------------------------------------------------
120              
121             #######
122             # Usage: $remote = new File::Remote;
123             #
124             # This constructs a new File::Remote object
125             #######
126              
127             sub new {
128             # Easy mostly-std new()
129 0     0 1   my $self = shift;
130 0   0       my $class = ref($self) || $self || $DefaultClass;
131              
132             # Add any options to our own defaults
133 0           my %opt = (@OPT, @_);
134 0           return bless \%opt, $class;
135             }
136              
137             #------------------------------------------------
138             # Private Functions (for public see "/__DATA__")
139             #------------------------------------------------
140              
141             #######
142             # Usage: my($self, @args) = _self_or_default(@_);
143             #
144             # This is completely stolen from the amazing CGI.pm. I did
145             # not write this!! Thanks, Lincoln Stein! :-)
146             #######
147              
148             sub _self_or_default {
149              
150 0 0 0 0     return @_ if defined($_[0]) && (!ref($_[0])) && ($_[0] eq 'File::Remote');
      0        
151 0 0 0       unless (defined($_[0]) && (ref($_[0]) eq 'File::Remote'
      0        
152             || UNIVERSAL::isa($_[0],'File::Remote'))) {
153 0 0         $DefaultClassObject = $DefaultClass->new unless defined($DefaultClassObject);
154 0           unshift(@_, $DefaultClassObject);
155             }
156 0           return @_;
157             }
158              
159             #######
160             # Usage: $tmpfile = $remote->_tmpfile($file);
161             #
162             # This sets a unique temp file for each $self/$file combo,
163             # which is used during remote rsh/rcp calls
164             #######
165              
166             sub _tmpfile {
167              
168 0     0     my($self, $file) = _self_or_default(@_);
169 0           $file =~ tr#[:/]#_#; # "fix" filename
170 0           my($tmpdir, $tmpfile);
171 0           $tmpdir = $self->settmp;
172              
173             # Have a little loop so that we don't collide w/ other File::Remote's
174 0           my $num = $$;
175 0           do {
176 0           $tmpfile = "$tmpdir/.rfile.$file.$num";
177 0           $num++;
178             } while (-f $tmpfile);
179 0           return $tmpfile;
180             }
181              
182             #######
183             # Usage: $remote->_system(@cmd) or return undef;
184             #
185             # Front-end for built-in firing off system commands to twiddle
186             # return vals. Here, we don't actually use system() because we
187             # need the appropriate return value so that $! makes sense.
188             #######
189              
190             sub _system {
191 0     0     my($self, @cmd) = _self_or_default(@_);
192              
193             # return "Broken pipe" if cmd invalid
194 0           chomp(my $return = `@cmd 2>&1 1>/dev/null || echo 32`);
195 0           _debug("_system(@cmd) = $return");
196              
197 0 0         if ($return) {
198             # if echo'ed an int (internal tests), use it, else use "Permission denied" (13)
199 0           $return =~ m/^(\d+)$/;
200 0   0       $! = $1 || 13;
201 0           return undef;
202             }
203 0           return 1;
204             }
205              
206             #######
207             # Usage: my($host, $file) = _parsepath($path);
208             #
209             # This is used to parse the $path param to look for host:/file
210             # This always returns an array, the deal is that if the file
211             # is remote, you get a host (arg1). Otherwise, it's undef.
212             #
213             # Thanks to David Robins and Rob Mah for their fixes to this sub.
214             #######
215              
216             sub _parsepath {
217              
218 0     0     my($self, $file) = _self_or_default(@_);
219 0           my($rhost, $rfile) = split ':', $file, 2;
220              
221 0 0         return(undef, $rhost) unless $rfile; # return the file if no colon (faster)
222 0 0 0       if ($hostname =~ /^$rhost(\.|$)/ && $rfile =~ /^\//) {
223 0           return(undef, $rfile); # file is actually local
224             }
225 0           return($rhost, $rfile); # file is remote after all
226             }
227            
228             #######
229             # Usage: $fh = _to_filehandle($thingy);
230             #
231             # This is so we can pass in a filehandle or typeglob to open(),
232             # and close(). This is my own bastardization of Perl's symbol
233             # tables, so if it be broke, let me know.
234             #######
235              
236             sub _to_filehandle {
237              
238 0     0     my($self, $thingy) = _self_or_default(@_);
239 0 0         return undef unless $thingy;
240              
241             #warn "to_fh($thingy)";
242              
243             # This is the majority - bareword filehandles
244 0 0         unless (ref $thingy) {
245 1     1   6 no strict 'refs';
  1         1  
  1         3010  
246 0 0         return \*$thingy if $thingy =~ /^\*/; # glob like '*main::FILE'
247 0   0       local *globby = join '::', caller(1) || 'main', $thingy;
248 0           return *globby;
249             }
250              
251             # Check for globrefs and FileHandle objects
252 0 0 0       return $thingy if UNIVERSAL::isa($thingy,'GLOB')
253             || UNIVERSAL::isa($thingy,'FileHandle');
254              
255 0           return undef;
256             }
257              
258             #------------------------------------------------
259             # Public functions - all are exportable
260             #------------------------------------------------
261              
262             # Everything down here should be SelfLoaded
263             # Can't use the SelfLoader because of conflicts with CORE::open
264             #__DATA__
265              
266             #######
267             # Usage: $remote->setXXX($value);
268             #
269             # These three functions are for setting necessary variables.
270             # All of them do sanity checks which will be called both when
271             # a variable is assigned as well as retrieved. This prevents
272             # "mass badness". If not value is passed, the current setting
273             # is returned (good for checking).
274             #######
275              
276             sub setrsh {
277             # Sets the variable $self->{rsh}, which is what to use for rsh calls
278 0     0 1   my($self, $value) = _self_or_default(@_);
279 0 0         $self->{rsh} = $value if $value;
280              
281             # This check was removed because of relative paths/speed.
282 0 0         if ($CHECK_RSH_IS_VALID) {
283 0 0         croak "setrsh() set to non-executable file '$self->{rsh}'"
284             unless (-x $self->{rsh});
285             }
286              
287 0           return $self->{rsh};
288             }
289            
290             sub setrcp {
291             # Sets the variable $self->{rcp}, which is what to use for rcp calls
292 0     0 1   my($self, $value) = _self_or_default(@_);
293 0 0         $self->{rcp} = $value if $value;
294              
295             # This check was removed because of relative paths/speed.
296 0 0         if ($CHECK_RCP_IS_VALID) {
297 0 0         croak "setrcp() set to non-executable file '$self->{rcp}'"
298             unless (-x $self->{rcp});
299             }
300              
301 0           return $self->{rcp};
302             }
303              
304             sub settmp {
305             # Sets the variable $self->{tmp}, which refs the temp dir needed to
306             # hold temporary files during rsh/rcp calls
307 0     0 1   my($self, $value) = _self_or_default(@_);
308 0 0         $self->{tmp} = $value if $value;
309 0 0         croak "settmp() set to non-existent dir '$self->{tmp}'"
310             unless (-d $self->{tmp});
311 0           return $self->{tmp};
312             }
313              
314              
315             #######
316             # Usage: $remote->open(FILEHANDLE, $file);
317             #
318             # opens file onto FILEHANDLE (or typeglob) just like CORE::open()
319             #
320             # There's one extra step here, and that's creating a hash that
321             # lists the open filehandles and their corresponding filenames.
322             # If anyone knows a better way to do this, LET ME KNOW! This is
323             # a major kludge, but is needed in order to copy back the changes
324             # made to remote files via persistent filehandles.
325             #######
326              
327             *ropen = \&open;
328             sub open {
329              
330 0     0 1   my($self, $handle, $file) = _self_or_default(@_);
331 0 0 0       croak "Bad usage of open(HANDLE, file)" unless ($handle && $file);
332              
333             # Private vars
334 0           my($f, $fh, $tmpfile);
335              
336             # Before parsing path, need to check for <, >, etc
337 0           $file =~ m/^([\<\>\|\+]*)\s*(.*)/;
338 0           $file = $2;
339 0   0       my $method = $1 || '<';
340              
341 0 0         croak "Unsupported file method '$method'" unless ($method =~ m/^\+?[\<\>\|]{1,2}$/);
342 0           my($rhost, $lfile) = _parsepath($file);
343              
344             # Catch for remote pipes
345 0 0 0       if (($method =~ m/\|/) && $rhost) {
346 0           croak "Sorry, File::Remote does not support writing to remote pipes"
347             }
348              
349             # Setup filehandle
350 0 0         $fh = _to_filehandle($handle) or return undef;;
351              
352             # Check if it's open already - if so, close it first like native Perl
353 0 0 0       if($RW_HANDLES{$fh} || $RW_HANDLES{$fh}) {
354 0 0         $self->close($handle) or return undef;
355             }
356              
357             # Check for local or remote files
358 0 0         if($rhost) {
359 0           $tmpfile = $self->_tmpfile($file);
360 0           $f = $tmpfile;
361              
362             # XXX Add this filehandle to our hash - this is a big kludge,
363             # XXX if there's something I'm missing please let me know!!!
364             # XXX This is so that on close(), the file can be copied back
365             # XXX over to the source to overwrite whatever's there.
366             # XXX Because of the performance hit, only add it if it's rw.
367 0 0         if ($method =~ m/\>/) {
368              
369             # First check to see if the remote file is writeable,
370             # but only if the variable $CHECK_REMOTE_FILES is on.
371             # Do our checks thru test calls that echo $! codes if
372             # they fail...
373              
374 0 0         if($CHECK_REMOTE_FILES) {
375 0           my $dir;
376 0           ($dir = $lfile) =~ s@(.*)/.*@$1@;
377 0 0         $self->_system($self->setrsh, $rhost,
378             "'if test -f $lfile; then
379             test -w $lfile || echo 13 >&2;
380             else
381             test -d $dir || echo 2 >&2;
382             fi'") or return undef;
383             }
384              
385 0           $RW_HANDLES{$fh} = $file;
386 0           $RW_TMPFILES{$file} = $tmpfile;
387             } else {
388             # push tmpfile onto an array
389 0           $RO_HANDLES{$fh} = $file;
390 0           $RO_TMPFILES{$file} = $tmpfile;
391             }
392              
393             # If we escaped that mess, copy our file over locally
394             # For open(), ignore failed copies b/c the file might be new
395 0           $self->copy($file, $tmpfile);
396              
397             } else {
398 0           $f = $lfile;
399             }
400              
401             # All we do is pass it straight thru to open()
402 0           local *fh = $fh;
403 0 0         CORE::open(*fh, "$method $f") or return undef;
404 0           return 1;
405             }
406              
407             #######
408             # Usage: $remote->open(FILEHANDLE, $file);
409             #
410             # closes FILEHANDLE and flushes buffer just like CORE::close()
411             #######
412              
413             *rclose = \&close;
414             sub close {
415              
416 0     0 1   my($self, $handle) = _self_or_default(@_);
417 0 0         croak "Bad usage of close(HANDLE)" unless ($handle);
418            
419             # Setup filehandle and close
420 0 0         my $fh = _to_filehandle($handle) or return undef;
421 0           local *fh = $fh;
422 0 0         CORE::close($fh) or return undef;
423              
424             # See if it's a writable remote handle
425 0 0         if(my $file = delete $RW_HANDLES{$fh}) {
426              
427             # If it's a remote file, we have extra stuff todo. Basically,
428             # we need to copy the local tmpfile over to the remote host
429             # which has the equivalent effect of flushing buffers for
430             # local files (as far as the user can tell).
431              
432 0           my($rhost, $lfile) = _parsepath($file);
433 0 0         if($rhost) {
434 0           my $tmpfile = delete $RW_TMPFILES{$file};
435 0 0         $self->copy($tmpfile, $file) or return undef;
436 0           CORE::unlink($tmpfile);
437             }
438             } else {
439 0           my $tmpfile = delete $RO_HANDLES{$fh};
440 0 0         delete $RO_TMPFILES{$tmpfile} if $tmpfile;
441             }
442 0           return 1;
443             }
444              
445             # This is a special method to close all open rw remote filehandles on exit
446             END {
447 1     1   177 for my $fh (keys %RW_HANDLES) {
448 0 0       0 carp "$fh remote filehandle left open, use close()" if ($^W);
449 0         0 &close($fh); # ignore errors, programmer should use close()
450             }
451 1         4 for my $tmpfile (values %RW_TMPFILES) {
452 0         0 CORE::unlink($tmpfile);
453             }
454 1         3 for my $fh (keys %RO_HANDLES) {
455 0         0 &close($fh);
456             }
457 1         24 for my $tmpfile (values %RO_TMPFILES) {
458 0           CORE::unlink($tmpfile);
459             }
460             }
461              
462             #######
463             # Usage: $remote->touch($file);
464             #
465             # "touches" a file (creates an empty one or updates mod time)
466             #######
467              
468             *rtouch = \&touch;
469             sub touch {
470 0     0 1   my($self, $file) = _self_or_default(@_);
471 0 0         croak "Bad usage of touch" unless ($file);
472 0           my($rhost, $lfile) = _parsepath($file);
473 0 0         if($rhost) {
474 0 0         $self->_system($self->setrsh, $rhost, "touch $lfile") or return undef;
475             } else {
476 0           local *F;
477 0 0         CORE::open(F, ">>$lfile") or return undef;
478             }
479 0           return 1;
480             }
481              
482              
483             #######
484             # Usage: @file = $remote->readfile($file);
485             #
486             # This reads an entire file and returns it as an array. In a
487             # scalar context the number of lines will be returned.
488             #######
489              
490             *rreadfile = \&readfile;
491             sub readfile {
492              
493 0     0 1   my($self, $file) = _self_or_default(@_);
494 0 0         croak "Bad usage of readfile" unless ($file);
495 0           my($rhost, $lfile) = _parsepath($file);
496              
497             # Private vars
498 0           my($f, $fh, $tmpfile);
499              
500             # Check for local or remote files
501 0 0         if($rhost) {
502 0           $tmpfile = $self->_tmpfile($file);
503 0 0         $self->copy($file, $tmpfile) or return undef;
504 0           $f = $tmpfile;
505             } else {
506 0           $f = $lfile;
507             }
508              
509             # These routines borrowed heavily from File::Slurp
510 0           local(*F);
511 0 0         CORE::open(F, "<$f") or return undef;
512 0           my @r = ;
513 0 0         CORE::close(F) or return undef;
514              
515             # Remove the local copy if it exists.
516             # Thanks to Neville Jennings for catching this.
517 0 0         CORE::unlink($tmpfile) if $tmpfile;
518              
519 0 0         return @r if wantarray;
520 0           return join("", @r);
521             }
522              
523             #######
524             # Usage: $remote->writefile($file, @file);
525             #
526             # This writes an entire file using the array passed in as
527             # the second arg. It overwrites any existing file of the
528             # same name. To back it up first, use backup().
529             #######
530              
531             *rwritefile = \&writefile;
532             sub writefile {
533              
534 0     0 1   my($self, $file, @data) = _self_or_default(@_);
535 0 0         croak "Bad usage of writefile" unless ($file);
536 0           my($rhost, $lfile) = _parsepath($file);
537              
538             # Private vars
539 0           my($f, $fh, $tmpfile);
540              
541             # Check for local or remote files
542 0 0         if($rhost) {
543 0           $tmpfile = $self->_tmpfile($file);
544 0           $f = $tmpfile;
545             } else {
546 0           $f = $lfile;
547             }
548            
549             # These routines borrowed heavily from File::Slurp
550 0           local(*F);
551 0 0         CORE::open(F, ">$f") or return undef;
552 0 0         print F @data or return undef;
553 0 0         CORE::close(F) or return undef;
554            
555             # Need to copy the file back over
556 0 0         if($rhost) {
557 0 0         if(-f $tmpfile) {
558 0 0         $self->copy($tmpfile, $file) or return undef;
559 0           CORE::unlink($tmpfile);
560             } else {
561 0           carp "File::Remote Internal Error: Attempted to write to $file but $tmpfile missing!";
562 0           return undef;
563             }
564             }
565              
566 0           return 1;
567             }
568              
569             #######
570             # Usage: $remote->mkdir($dir, $mode);
571             #
572             # This creates a new dir with the specified octal mode.
573             #######
574              
575             *rmkdir = \&mkdir;
576             sub mkdir {
577              
578             # Local dirs go to mkpath, remote to mkdir -p
579 0     0 1   my($self, $dir, $mode) = _self_or_default(@_);
580 0 0         croak "Bad usage of mkdir" unless ($dir);
581 0           my($rhost, $ldir) = _parsepath($dir);
582             #$mode = '0755' unless $mode;
583              
584 0 0         if($rhost) {
585 0 0         $self->_system($self->setrsh, $rhost, "'mkdir -p $ldir'") or return undef;
586             } else {
587 0 0         mkpath(["$ldir"], 0, $mode) || return undef;
588             }
589 0           return 1;
590             }
591              
592             #######
593             # Usage: $remote->rmdir($dir, $recurse);
594             #
595             # This removes the specified dir.
596             #######
597              
598             *rrmdir = \&rmdir;
599             sub rmdir {
600              
601 0     0 1   my($self, $dir, $recurse) = _self_or_default(@_);
602 0 0         croak "Bad usage of rmdir" unless ($dir);
603 0           my($rhost, $ldir) = _parsepath($dir);
604 0 0         $recurse = 1 unless defined($recurse);
605              
606 0 0         if($rhost) {
607 0 0         if ($recurse) {
608 0 0         $self->_system($self->setrsh, $rhost, "rm -rf $ldir") or return undef;
609             } else {
610 0 0         $self->_system($self->setrsh, $rhost, "rmdir $ldir") or return undef;
611             }
612             } else {
613 0 0         if ($recurse) {
614 0 0         rmtree(["$ldir"], 0, 0) or return undef;
615             } else {
616 0 0         rmdir $ldir or return undef;
617             }
618             }
619 0           return 1;
620             }
621            
622             #######
623             # Usage: $remote->copy($file1, $file2);
624             #
625             # This copies files around, just like UNIX cp. If one of
626             # the files is remote, it uses rcp. Both files cannot be
627             # remote.
628             #######
629              
630             *rcp = \©
631             *rcopy = \©
632             *cp = \©
633             sub copy {
634             # This copies the given file, either locally or remotely
635             # depending on whether or not it's remote or not.
636 0     0 1   my($self, $srcfile, $destfile) = _self_or_default(@_);
637 0 0 0       croak "Bad usage of copy" unless ($srcfile && $destfile);
638 0           my($srhost, $slfile) = _parsepath($srcfile);
639 0           my($drhost, $dlfile) = _parsepath($destfile);
640              
641 0 0 0       if($srhost || $drhost) {
642 0           _debug("copy -- system($self->setrcp, $srcfile, $destfile)");
643 0 0         $self->_system($self->setrcp, $srcfile, $destfile) or return undef;
644             } else {
645 0           _debug("copy -- copy($slfile, $dlfile)");
646 0 0         File::Copy::copy($slfile, $dlfile) or return undef;
647             }
648 0           return 1;
649             }
650              
651             #######
652             # Usage: $remote->move($file1, $file2);
653             #
654             # This moves files around, just like UNIX mv. If one of
655             # the files is remote, it uses rcp/rm. Both files cannot be
656             # remote.
657             #######
658              
659             *rmove = \&move;
660             *rmv = \&move;
661             *mv = \&move;
662             sub move {
663              
664             # This does NOT fall through to a standard rename command,
665             # simply because there are too many platforms on which this
666             # works too differently (Solaris vs. Linux, for ex).
667              
668 0 0 0 0 1   (©(@_) && &unlink(@_)) || return undef;
669 0           return 1;
670             }
671              
672             #######
673             # Usage: $remote->chown($file1, $file2);
674             #
675             # This chown's files just like UNIX chown.
676             #######
677              
678              
679             *rchown = \&chown;
680             sub chown {
681              
682             # If remote, subshell it; else, use Perl's chown
683             # Form of chown is the same as normal chown
684 0     0 1   my($self, $uid, $gid, $file) = _self_or_default(@_);
685 0 0 0       croak "Bad usage of chown" unless ($uid && $gid && $file);
      0        
686 0           my($rhost, $lfile) = _parsepath($file);
687              
688 0 0         if($rhost) {
689 0 0         $self->_system($self->setrsh, $rhost, "'chown $uid $lfile ; chgrp $gid $lfile'") or return undef;
690             } else {
691             # Check if we need to resolve stuff
692 0 0         ($uid) = getpwnam($uid) if ($uid =~ /[a-zA-Z]/);
693 0 0         ($gid) = getgrnam($gid) if ($gid =~ /[a-zA-Z]/);
694 0 0         chown($uid, $gid, $lfile) || return undef;
695             }
696 0           return 1;
697             }
698              
699             #######
700             # Usage: $remote->chmod($mode, $file);
701             #
702             # This chmod's files just like UNIX chmod.
703             #######
704              
705             *rchmod = \&chmod;
706             sub chmod {
707              
708             # Same as chown, really easy
709 0     0 1   my($self, $mode, $file) = _self_or_default(@_);
710 0 0 0       croak "Bad usage of chmod" unless ($mode && $file);
711 0           my($rhost, $lfile) = _parsepath($file);
712              
713 0 0         if($rhost) {
714 0 0         $self->_system($self->setrsh, $rhost, "'chmod $mode $lfile'") or return undef;
715             } else {
716 0 0         chmod($mode, $lfile) || return undef;
717             }
718 0           return 1;
719             }
720              
721             #######
722             # Usage: $remote->unlink($file);
723             #
724             # This removes files, just like UNIX rm.
725             #######
726              
727             *rrm = \&unlink;
728             *rm = \&unlink;
729             *runlink = \&unlink;
730             sub unlink {
731              
732             # Really easy
733 0     0 1   my($self, $file) = _self_or_default(@_);
734 0 0         croak "Bad usage of unlink" unless ($file);
735 0           my($rhost, $lfile) = _parsepath($file);
736              
737 0 0         if($rhost) {
738 0 0         $self->_system($self->setrsh, $rhost, "'rm -f $lfile'") or return undef;
739             } else {
740 0 0         CORE::unlink($lfile) || return undef;
741             }
742 0           return 1;
743             }
744              
745             #######
746             # Usage: $remote->link($file);
747             #
748             # This links files, just like UNIX ln.
749             #######
750              
751             *rln = \&link;
752             *ln = \&link;
753             *rlink = \&link;
754             sub link {
755              
756             # This logic is similar to copy, only if a host:/path
757             # is specified, that must be specified for both - we
758             # can't link across servers! (obviously)
759 0     0 1   my($self, $srcfile, $destfile) = _self_or_default(@_);
760 0 0 0       croak "Bad usage of link" unless ($srcfile && $destfile);
761 0           my($srhost, $slfile) = _parsepath($srcfile);
762 0           my($drhost, $dlfile) = _parsepath($destfile);
763              
764 0 0 0       if($srhost && $drhost) {
    0 0        
765 0 0         if($srhost eq $drhost) {
766 0 0         $self->_system($self->setrsh, $srhost, "ln", $slfile, $dlfile) or return undef;
767             } else {
768 0           croak "Cannot link two files from different hosts!";
769             }
770             } elsif($srhost || $drhost) {
771 0           croak "Cannot link two files from different hosts!";
772             } else {
773 0 0         CORE::link($slfile, $dlfile) or return undef;
774             }
775 0           return 1;
776             }
777              
778             #######
779             # Usage: $remote->symlink($file);
780             #
781             # This symlinks files, just like UNIX ln -s.
782             #######
783              
784             *rsymlink = \&symlink;
785             sub symlink {
786              
787             # This logic is similar to copy, only if a host:/path
788             # is specified, that must be specified for both - we
789             # can't link across servers! (obviously)
790 0     0 1   my($self, $srcfile, $destfile) = _self_or_default(@_);
791 0 0 0       croak "Bad usage of symlink" unless ($srcfile && $destfile);
792 0           my($srhost, $slfile) = _parsepath($srcfile);
793 0           my($drhost, $dlfile) = _parsepath($destfile);
794              
795 0 0 0       if($srhost && $drhost) {
    0 0        
796 0 0         if($srhost eq $drhost) {
797 0 0         $self->_system($self->setrsh, $srhost, "ln -s", $slfile, $dlfile) or return undef;
798             } else {
799 0           croak "Cannot symlink two files from different hosts!";
800             }
801             } elsif($srhost || $drhost) {
802 0           croak "Cannot symlink two files from different hosts!";
803             } else {
804 0 0         CORE::symlink($slfile, $dlfile) or return undef;
805             }
806 0           return 1;
807             }
808              
809             #######
810             # Usage: $remote->readlink($file);
811             #
812             # This reads what a symbolic link points to
813             #######
814              
815             *rreadlink = \&readlink;
816             sub readlink {
817              
818 0     0 1   my($self, $file) = _self_or_default(@_);
819 0 0         croak "Bad usage of readlink" unless ($file);
820 0           my($rhost, $lfile) = _parsepath($file);
821              
822 0 0         if ($rhost) {
823             # this command is a little tricky, and not guaranteed
824             # to be 100% portable... note that we can't even use
825             # the _system() internal function because it's so weird...
826 0           my $rsh = $self->setrsh;
827 0           chomp(my $path = `$rsh $rhost "ls -l $lfile | awk '{print \$NF}' || echo NOPE" 2>/dev/null`);
828 0 0         if ($path eq 'NOPE') {
829 0           $! = 2;
830 0           return undef;
831             } else {
832 0           return $path;
833             }
834             } else {
835 0           return CORE::readlink($lfile);
836             }
837 0           return undef;
838             }
839              
840             #######
841             # Usage: $remote->backup($file, $suffix|$filename);
842             #
843             # Remotely backs up a file. A little tricky, but not too much.
844             # If the file is remote we just do a 'rcp -p'. If it's local,
845             # we do a cp, along with some stat checks. The cool thing about
846             # this function is that it takes two arguments, the second
847             # can be either a suffix (like '.bkup') or a full file name
848             # (like '/local/backups/myfile'), and the function does the
849             # appropriate thing. If will also accept a 'host:/dir/file'
850             # arg as the suffix, which means you can do this:
851             #
852             # rbackup('mainhost:/dir/file', 'backuphost:/dir/new/file');
853             #######
854              
855             *rbackup = \&backup;
856             sub backup {
857              
858 0     0 1   my($self, $file, $suffix) = _self_or_default(@_);
859 0 0         croak "Bad usage of backup" unless ($file);
860 0   0       $suffix ||= 'bkup';
861              
862 0           my($rhost, $lfile) = _parsepath($file);
863 0           my($bhost, $bfile) = _parsepath($suffix);
864              
865             # See if the thing is a suffix or filename
866 0 0         $bfile = "$file.$suffix" unless ($bfile =~ m@/@); # a path name
867              
868             # All we do now if drop thru to our own copy routine
869 0           _debug("backup() calling copy($file, $bfile)");
870 0 0         $self->copy($file, $bfile) or return undef;
871 0           return 1;
872             }
873              
874             #######
875             # Usage: $remote->append($file, @file);
876             #
877             # This is just like writefile, only that it appends to the file
878             # rather than overwriting it.
879             #######
880              
881             *rappend = \&append;
882             sub append {
883 0     0 1   my($self, $file, @file) = _self_or_default(@_);
884 0 0         croak "Bad usage of append" unless ($file);
885 0 0         my @prefile = $self->readfile($file) or return undef;
886 0 0         my @newfile = (@prefile, @file) or return undef;
887 0 0         $self->writefile($file, @newfile) or return undef;
888 0           return 1;
889             }
890              
891             #######
892             # Usage: $remote->prepend($file, @file);
893             #
894             # This is just like writefile, only that it prepends to the file
895             # rather than overwriting it.
896             #######
897              
898             *rprepend = \&prepend;
899             sub prepend {
900 0     0 1   my($self, $file, @file) = _self_or_default(@_);
901 0 0         croak "Bad usage of prepend" unless ($file);
902 0 0         my @postfile = $self->readfile($file) or return undef;
903 0 0         my @newfile = (@file, @postfile) or return undef;
904 0 0         $self->writefile($file, @newfile) or return undef;
905 0           return 1;
906             }
907              
908             1;
909              
910             #------------------------------------------------
911             # Documentation starts down here...
912             #------------------------------------------------
913              
914             __END__ DATA